Compare commits

..

25 Commits
beta ... stable

Author SHA1 Message Date
abdussamedulutas cbe424f3e3 Styling 2025-07-01 21:26:31 +03:00
abdussamedulutas b66adffd3a Grid analyze 2025-06-16 02:50:27 +03:00
abdussamedulutas 3ebf5cc1f5 Grid analyze 2025-06-16 02:44:47 +03:00
abdussamedulutas c6c42d4bd8 Peer xclient bug fixed 2025-03-21 22:30:07 +03:00
Abdussamed 640bff88c0 Reversed 2025-02-13 23:01:32 +03:00
Abdussamed 7becdd5a80 Hand 2025-01-16 20:34:03 +03:00
AbdussamedULUTAŞ a19ae8a746 Recaiving stream added 2024-04-17 16:02:46 +03:00
AbdussamedULUTAŞ 07ee1d4db0 Recaiving stream added 2024-04-17 16:02:27 +03:00
AbdussamedULUTAŞ 999274e946 Syntax error 'id' not allowed error fixed 2024-04-15 11:19:39 +03:00
Abdussamed 912f32e7ab Safely send disconnected peers 2024-04-05 10:57:16 +03:00
Abdussamed eeee824b20 Syntax error fixed 2024-04-05 10:30:39 +03:00
Abdussamed 8d3c116a0b Syntax error fixed 2024-04-05 10:28:07 +03:00
Abdussamed 6affbf19be Build bundle 2024-04-05 10:21:38 +03:00
Abdussamed 4cd68f4a7f Disconnected pair event added 2024-04-05 10:06:23 +03:00
Abdussamed 73b5ade3e1 Syntax fix 2024-04-03 16:03:02 +03:00
Abdussamed 8d076456e3 Syntax error 2024-04-03 04:45:54 +03:00
Abdussamed 2439907c59 Connection bug fix / More than debugging 2024-03-21 09:25:31 +03:00
saqut 63f054f6bf README.md Güncelle 2024-03-19 01:15:08 +03:00
Abdussamed 88d87c2c68 Stabilization 234 2024-03-19 01:14:20 +03:00
Abdussamed e9e7ebbbc1 Bugfix 2024-03-04 10:51:17 +03:00
Abdussamed 5175c2b456 Bug fix patch 2024-02-11 23:32:44 +03:00
Abdussamed 870ec96e9a Removed unusal files 2024-02-11 20:39:41 +03:00
saqut 3b3b480b72 README.md Güncelle 2024-02-10 22:30:12 +03:00
saqut 9325bf645f README.md Güncelle 2024-02-10 21:48:34 +03:00
Abdussamed 5c30f871c7 Frontend Connection yapılandırması 2024-02-10 18:16:22 +03:00
48 changed files with 2243 additions and 4006 deletions

View File

@ -1 +0,0 @@
# Please fill in the URLrewrite rules or custom Apache config here

View File

@ -1 +0,0 @@
xldcAYGIOnxW2yiX6KDZUW44-JTev8gg7r3kzrwY34k.PZDupKjVmMf0TNd3vLGxHErgeolq5CbdlHehVDWziwE

301
README.md
View File

@ -6,301 +6,18 @@ Servis, bağlantı sağlayan cihazların verilerini kendi aralarında senkron et
Bağlantı TCP tabanlı yüksek hızlı WebSocket protokolüne dayanır ve sunucunun cihazları sanallaştırması sayesinde diğer kişilerin IP adreslerini veya cihaz türü gibi bilgilere ihtiyaç duymadan düşük gecikmeli çift taraflı serbest iletişim kurmalarını sağlar.
[Geliştirici Dökümanı](https://git.saqut.com/saqut/MWSE/wiki/Entegrasyon)
# Güvenlik !
Framework, bağlı tüm cihazlar arasında mesajları doğru hedefe, verinin bozulmadığını garanti ederek iletmekden sorumludur.
Bunların dışında hassas verilerin soket üzerinden iletilmesi şimdilik önerilmez, clientlerin ileteceği mesajlar **SOKETE İLETİLMEDEN ÖNCE** kullanıcılar tarafından manipüle edilebilir veya taklit edilebilir ve MWSE bunun doğrulamasını **YAPMAZ**
## WebSocket topolojisi
![image](https://www.hitechmv.com/wp-content/uploads/2014/05/startopology.jpg)
## Proje tarafından uygulanan load balance teknolojisi
![image](https://git.saqut.com/saqut/storage/raw/branch/master/Diagram1.png)
# Geliştirici Dökümantasyonu
## Kurulum
### Proje ortamına kurulumu
```html
<script src="https://ws.saqut.com/script"></script>
```
### Geliştirme ortamına kurulumu
```javascript
const mwse = new MWSE({
endpoint: "wss://ws.saqut.com/" // MSWS kurulu sunucu adresi
});
mwse.scope(async () => {
// Bağlantı sağlandığında burası tetiklenir
})
```
### Kendi bağlantı kimliğini öğrenme
```javascript
mwse.scope(async () => {
let me = mwse.peer('me'); // Kendi bağlantınız üzerinde işlem yaparken `me` olarak bahsedersiniz
console.log(me.socketId); // Her eşin tekil bir socketIdsi vardır
})
```
### Sanal Adres ayırma / yeniden ayırma / kaldırma
```javascript
mwse.scope(async () => {
let me = mwse.peer('me');
/**
* Sanal adresler size veri gönderilmek istendiğinde veya etkileşime
* geçilmesi istendiğinde ona socketId gibi bir UUID yerine sizi temsil eden daha kısa
* ip adresi, sayı veya kısa bir kod ile aynı şeyleri yapmanıza olanak tanır.
* Aynı anda hem sanal ip adres, sayı ve kısa koduna sahip olabilirsiniz
* ancak aynı türden temsil koduna (mesela kısa koddan) birden fazla sahip olamazsınız
* Yeni bir bağlantı daha açmanız gerekir
**/
// Bağlantınıze özel sanal tekil ip adresi kaynağı ayırın
let ipadress = await me.virtualPressure.allocAPIPAddress();
// Bağlantınıze özel sanal tekil numara kaynağı ayırın
let numberaddress = await me.virtualPressure.allocAPNumber();
// Bağlantınıze özel sanal kod kaynağı ayırın
let shortcodeadress = await me.virtualPressure.allocAPShortCode();
// Bütün bu kaynakları yenileriyle değiştirmek için
// her birinin ayrı ayrı yeniden alma işlevleri vardır
// Bir adresi yenilediğinizde artık eski adres kullanılmaz olur
me.virtualPressure.reallocAPIPAddress();
me.virtualPressure.reallocAPNumber();
me.virtualPressure.reallocAPShortCode();
// Bütün bu kaynakları kaldırmak için her birinin ayrı ayrı
// bırakma işlevi vardır
// Bir adresi kullanmadığınızda artık bu adreslerden size
// ulaşılamaz olursunuz
await me.virtualPressure.releaseAPIPAddress();
await me.virtualPressure.releaseAPNumber();
await me.virtualPressure.releaseAPShortCode();
await me.virtualPressure.queryAPIPAddress();
await me.virtualPressure.queryAPNumber();
await me.virtualPressure.queryAPShortCode();
})
```
### Farklı bir eşe erişme
```javascript
mwse.scope(async () => {
let peer = mwse.peer('325a8f7f-eaaf-4c21-855e-9e965c0d5ac9') // Diğer eşin socketId'sini belirtiyoruz
let me = mwse.peer('me');
// Eşin ulaşılabilir (online) olup olmadığını verir
let isOnline = await peer.isReachable();
if(isOnline)
{
// İleitşim kurmak için istek gönderiyoruz
await peer.requestPair();
}
// Bize gönderilecek olan istekleri dinliyoruz
me.on('request/pair', peer => {
// İstek gönderen kullanıcının bilgileri peer içinde bulunur
// iletişime devam etmek için isteği kabul ediyoruz
peer.acceptPair();
// veya istemiyorsak reddediyoruz
peer.rejectPair();
})
// Karşımızdaki kullanıcının ne cevap verdiğini
// anlamak için sistem ayrı ayrı cevaplar üretir
// Kabul ettiyse accept eventini tetikler
me.on('accepted/pair', peer => {
// İstek gönderen kullanıcının bilgileri peer içinde bulunur
})
// Reddettiyse veya iletişimi sonlandırmışsa
// end eventi tetiklenir
me.on('end/pair', peer => {
// Kullanıcının bilgileri peer içinde bulunur
})
})
```
Eğer uygulamanızda bu şekilde bir erişim metodolojisi kullanmak istemiyorsanız
pair sistemini elle kapatabilirsiniz
```javascript
mwse.scope(async () => {
let me = mwse.peer('me');
// Tüm kullanıcılar size mesaj iletebilir
await peer.disablePairAuth();
// Sadece eşleşmiş kullanıcılar size mesaj iletebilir
await peer.enablePairAuth();
})
```
## Tünelleme ile mesajlaşma
MWSE üzerinde karşılıklı mesajlaşma için 2
metodoloji bulunur bunlardan birisi serbest mesajlaşmadır ve
bu şekilde çalışır
```javascript
mwse.scope(async () => {
let me = mwse.peer('325a8f7f-eaaf-4c21-855e-9e965c0d5ac9');
// Tüm kullanıcılar size mesaj iletebilir
await peer.disablePairAuth();
// Bu şekilde serbest bir şekilde herhangi bir zamanda karşılıksız mesaj iletebilirsiniz
peer.send({
text: "Good morning"
})
// Kullanıcı her hangi bir mesaj gönderdiğinde burası tetiklenir
peer.on('message', message => {
if(text.message == "Good morning")
{
// Eğer mesaj için bir cevap bekliyorsa cevap veriyoruz
peer.send("You are welcome");
}else{
// Ancak mesaj önceden gönderdiğimiz isteğin cevabıysa görüntülüyoruz
console.log("Reply is :", message) // --> You are welcome
}
})
})
```
2. yöntem ise el sıkışmalı mesajlaşmadır ve gönderdiğiniz bir mesaja verilen karşılık olarak
sistem, gelen mesajın gönderdiğiniz hangi mesaja karşılık olarak gönderildiğini tuttuğu için
request/response şeklinde ilerleyebilir
```javascript
mwse.scope(async () => {
let peer = mwse.peer('325a8f7f-eaaf-4c21-855e-9e965c0d5ac9');
let me = mwse.peer('me');
// Tüm kullanıcılar size mesaj iletebilir
await me.disablePairAuth();
// Bizden istenecek veriler için önceden cevapları hazırlıyoruz
me.on('request', ({body, response}) => {
switch(body.message)
{
case "Good morning":{
// do anything...
response("You are welcome")
break;
}
}
})
// Bu şekilde serbest bir şekilde herhangi bir zamanda karşılıksız mesaj iletebilirsiniz
let response = await peer.request({
message: "Good morning"
});
console.log(response) // response ---> You are welcome
})
```
Bu şekilde hem okunabilirlik artar hemde mesajlar karşılıklı olarak etki-tepki şeklinde ilerler
----
### Oda kurma ve kapatma
Oda sistemi kullanıcıların bir araya gelerek, tek seferde bir grubun
toplu olarak birbirleriyle mesajlaşabileceği bir yapıdır.
Kişiler birbirlerini bulabilir, toplu mesajlar iletebilirler, odaya
katılabilir veya ayrılabilirler
Odanın ayarlarını, odayı ilk oluşturan kişi belirler ve oda bir kez oluşturulduğunda yeniden aynı isimle oda açılamaz
Oda türü (joinType) herkese açık `free`, davet ile `invite`, şifreli `password` veya herkese kapalı `lock` olabilir.
```javascript
mwse.scope(async () => {
let room = mwse.room({
name: "Oda ismi", // odanın görünür ismi
description: "Oda açıklaması", // odanın görünecek ismi
joinType: "free", // herkese açık oda
credential: "****", // varsa şifre
notifyActionInvite: false, // yeni biri katılmak istediğinde tüm peerlere haber iletme özelliği
notifyActionJoined: true, // yeni biri katıldığında tüm peerlere haber iletir
notifyActionEjected: true, // Biri odadan ayrıldığında tüm peerlere haber iletir
ifexistsJoin: false // true ayarlanırsa, oluşturma aşamasında oda zaten var yanıtı alırsa hata vermek yerine odaya katılır
});
// Verilen isime ait odaya katılmak için bu komut yeterlidir
await room.join(); // --> odaya katılırken sorun oluşursa hata fırlatır
// Verilen ayarlara sahip odayı oluşturmak için aşağıdaki komut yeterlidir
await room.createRoom(); // -> oda oluştururken sorun oluşursa hata fırlatır
// Var olan bir odadan ayrılmak için bu komut kullanılabilir
await room.eject();
// İçinde bulunduğu odanın tüm eşlerinin idsini verir
let peers = await room.fetchPeers();
});
```
### Oda içerisinde iletişim
Odaya katıldıktan sonra katılan kişiler `room` üzerinden mesaj iletebilirsiniz, verilen her mesajı sistem istisnasız tüm oda katılımcılarına iletmekten sorumludur
Oda içerisindeki kişiler ile oda içindeki kişilerin listesini alarak özel iletişimede geçebilirsiniz ancak aynı odadaki kişiler sistem tarafında yinede güvenilir kabul edilmez
Sistem pairAuth güvenliği kapalı olan kişilere mesajlar iletir ancak açık olan kullanıcıların her biri için ayrı ayrı eşleşme isteği göndermelisiniz
```javascript
mwse.scope(async () => {
let room = mwse.room({
name: "Oda ismi", // odanın görünür ismi
description: "Oda açıklaması", // odanın görünecek ismi
joinType: "free", // herkese açık oda
notifyActionJoined: true, // yeni biri katıldığında tüm peerlere haber iletir
ifexistsJoin: false // true ayarlanırsa, oluşturma aşamasında oda zaten var yanıtı alırsa hata vermek yerine odaya katılır
});
// Verilen ayarlara sahip odayı oluşturmak için aşağıdaki komut yeterlidir
await room.createRoom(); // -> oda oluştururken sorun oluşursa hata fırlatır
// Oda içerisinde mesaj gönderildiğinde gönderilen paketi dinlemek için aşağıdaki komut kullanılabilir
room.on('message', message => {
// Odaya `message` paketi iletildi
})
// Odaya yeni birisi katıldığında haber almak için aşağıdaki komut kullanılabilir
room.on('join', peer => {
// Odaya katılan kişinin bilgisi peerde tutulur
// Odaya katılan kişiyle bağlantı kurup, oda dışında kendisiyle iletişimede geçebilirsiniz
peer.requestPair();
})
// Odaya birisi ayrıldığında haber almak için aşağıdaki komut kullanılabilir
room.on('eject', peer => {
// Odadan ayrılan kişinin bilgisi peerde tutulur
})
// Odadaki herkese mesaj iletmek için room içerisindeki send komutu kullanılabilir
room.send({
message: "Good year !"
});
// Oda kapatıldığında close tetiklenir
room.on('close', () => { })
});
```
### Odayı kapatma
Odalar odaya ait olan bağlantılar üzerine kurulur, tüm bağlantılar odadan çıktığında oda otomatik olarak kapatılır
![image](https://git.saqut.com/saqut/storage/raw/branch/master/Diagram1.png)

View File

@ -1,5 +1,5 @@
const { CLIENT_SEND_MESSAGE, CLIENT_UPDATE_PROP } = require("../IPC");
const stats = require("../stats");
const { CLIENT_SEND_MESSAGE, CLIENT_UPDATE_PROP } = require("./IPC");
const stats = require("./stats");
function Client()
{
/**
@ -57,13 +57,13 @@ Client.clients = new Map();
*/
Client.prototype.peerRequest = function(client){
let info = {};
this.store.forEach((value, name) => info[name] = value);
this.info.forEach((value, name) => info[name] = value);
this.pairs.add(client.id);
this.sync('pairs');
client.send([{
from: this.id,
info
},'request/pair']);
client.send([
{ from: this.id },
'request/pair'
]);
};
Client.prototype.match = function(filterObject){
@ -101,6 +101,7 @@ Client.prototype.isSecure = function(client)
client = Client.clients.get(client);
}else return false;
}else if(!(client instanceof Client)){
console.error("isSecure Client bir client veri tipinde değil")
return false;
};
@ -124,7 +125,7 @@ Client.prototype.isSecure = function(client)
return false;
}
/**
* @returns {{pairs:Map<string, Client>,roompairs:Map<string, Client>}}
* @returns {{pairs:Map<string, Client>,roompairs:Map<string, Client>,intersection:Map<string, Client>}}
*/
Client.prototype.getSucureClients = function()
{
@ -132,9 +133,9 @@ Client.prototype.getSucureClients = function()
let pairs = new Map();
let roompairs = new Map();
for (const [id, client] of this.pairs)
for (const id of this.pairs)
{
map.set(id, client)
pairs.set(id, Client.clients.get(id))
}
// Aynı odada bulunan kullanıcı
@ -151,7 +152,11 @@ Client.prototype.getSucureClients = function()
};
return {
pairs,
roompairs
roompairs,
intersection : new Map([
...pairs,
...roompairs
])
};
}
@ -200,8 +205,30 @@ Client.prototype.send = function(obj){
CLIENT_SEND_MESSAGE(this.id, obj, this.proxyProcess)
}else{
stats.ws_sended_packs++;
this.socket.sendUTF(JSON.stringify(obj));
if(this.socket.connected){
this.socket.sendUTF(JSON.stringify(obj),err => {
if(err && this.socket)
{
console.error("I/O: Hatalı yazma işlemi yapıldı",err.message)
}
});
}else{
console.error("Bağlantısı kopmuş yazma işlemi")
}
}
};
Client.prototype.packWriteable = function(){
return !!this.store.get("packrecaive")
}
Client.prototype.packReadable = function(){
return !!this.store.get("packsending")
}
Client.prototype.peerInfoNotifiable = function(){
return !!this.store.get("notifyPairInfo")
}
Client.prototype.roomInfoNotifiable = function(){
return !!this.store.get("notifyRoomInfo")
}
exports.Client = Client;

View File

@ -6,25 +6,16 @@ let compression = require("compression");
let {resolve} = require("path");
let auth = require("express-basic-auth");
const {
termoutput,
HTTP_PORT,
NETWORK_MASK,
HTTP_AUTH,
HTTP_AUTH_USERNAME,
HTTP_AUTH_USERPASSWORD
} = require("./config");
const { termoutput } = require("./config");
let server = http.createServer();
const stats = require("./stats");
let app = express();
server.addListener("request", app);
app.use(compression({
level: 9
}));
server.listen(HTTP_PORT,NETWORK_MASK,() => {
server.listen(7707,'0.0.0.0',() => {
termoutput && console.log("HTTP Service Running...");
});
server.addListener("error",(err)=> {
@ -32,13 +23,6 @@ server.addListener("error",(err)=> {
})
exports.http = server;
let authorize = auth({
users:{
[HTTP_AUTH_USERNAME]: HTTP_AUTH_USERPASSWORD
},
challenge: true
});
app.get("/script",(request, response)=>{
response.sendFile(resolve("./script/index.js"))
});
@ -48,19 +32,25 @@ app.get("/test",(request, response)=>{
app.get("/index.js.map",(request, response)=>{
response.sendFile(resolve("./script/index.js.map"))
});
if(HTTP_AUTH)
{
app.get("/stream",(request, response)=>{
response.sendFile(resolve("./public/index.html"))
});
app.use("/stream",express.static(resolve("./public")));
app.get("/",authorize,(request, response)=>{
response.sendFile(resolve("./script/index.html"))
});
app.post("/stats",authorize,(request, response)=>{
response.json(stats.others);
});
}
app.get("/stream",(request, response)=>{
response.sendFile(resolve("./public/index.html"))
});
app.get("/",(request, response)=>{
response.sendFile(resolve("./script/index.html"))
});
app.post("/stats",(request, response)=>{
response.json(stats.others);
});
app.get("/console",(request, response)=>{
response.sendFile(resolve("./console/index.html"))
});
app.get("/console/lib.js",(request, response)=>{
setTimeout(()=> {
response.sendFile(resolve("./console/lib.js"))
}, Math.random() * 5_000 | 0)
});
app.use("/stream",express.static(resolve("./public")));
app.use("/console/",express.static(resolve("./console")));
app.get("*",(request, response)=>{
response.sendFile(resolve("./script/status.xml"))

View File

@ -1,6 +1,6 @@
process.on('message',data => {
const { Client } = require("./Services/Client");
const { Client } = require("./Client.js");
const { Room } = require("./Services/Room");
switch(data.type)
{
@ -10,10 +10,18 @@ process.on('message',data => {
client.isProxy = true;
client.proxyProcess = data.pid;
client.id = data.uuid;
Client.clients.set(client.id, client);
if(Client.clients.has(client.id))
{
console.error("IPC: Zaten var olan kullanıcı oluşturuluyor")
}else{
Client.clients.set(client.id, client);
}
break;
}
case "CLIENT_UPDATE_PROP":{
data.value = transformDeserialization(data.value, data.typing);
slog("CLIENT_UPDATE_PROP");
let client = Client.clients.get(data.uuid);
client[data.name] = data.value;
@ -22,15 +30,27 @@ process.on('message',data => {
case "CLIENT_SEND_MESSAGE":{
//slog("CLIENT_SEND_MESSAGE");
let client = Client.clients.get(data.uuid);
if(client.isProxy != true)
if(client)
{
client.send(data.message)
if(client.isProxy != true)
{
client.send(data.message)
}else{
console.error("IPC: Proxy olmayan bir client için IPC mesajı alındı")
}
}else{
console.error("IPC: Olmayan bir kullanıcı için mesaj gönderiliyor")
}
break;
}
case "CLIENT_DESTROY":{
slog("CLIENT_DESTROY");
Client.clients.delete(data.uuid);
if(Client.clients.has(data.uuid))
{
Client.clients.delete(data.uuid);
}else{
console.error("IPC: Olmayan bir kullanıcı için silme gerçekleştiriliyor")
}
break;
}
case "ROOM_CREATED":{
@ -40,6 +60,10 @@ process.on('message',data => {
break;
}
case "ROOM_UPDATE_PROP":{
data.value = transformDeserialization(data.value, data.typing);
slog("ROOM_UPDATE_PROP");
let room = Room.rooms.get(data.uuid);
room[data.name] = data.value;
@ -86,11 +110,16 @@ function CLIENT_CREATED(uuid)
function CLIENT_UPDATE_PROP(uuid, name, value)
{
mlog("CLIENT_UPDATE_PROP");
let typing = value.__proto__.constructor.name;
value = transformSerialization(value);
process.send({
type:'CLIENT_UPDATE_PROP',
uuid: uuid,
name,
value
value,
typing
})
};
function CLIENT_SEND_MESSAGE(uuid, message, clusterPid)
@ -126,11 +155,16 @@ function ROOM_CREATED(room)
function ROOM_UPDATE_PROP(uuid, name, value)
{
mlog("ROOM_UPDATE_PROP");
let typing = value.__proto__.constructor.name;
value = transformSerialization(value);
process.send({
type:'ROOM_UPDATE_PROP',
uuid: uuid,
name,
value
value,
typing
})
};
@ -173,6 +207,31 @@ function slog(command)
console.log("S",process.pid, command)
}
function transformSerialization(value)
{
switch(value.__proto__.constructor.name)
{
case "Map":{
return [...value];
}
case "Set":{
return [...value];
}
}
}
function transformDeserialization(value,type)
{
switch(type)
{
case "Map":{
return new Map(value);
}
case "Set":{
return new Set(value)
}
}
}
exports.CLIENT_CREATED = CLIENT_CREATED;
exports.CLIENT_UPDATE_PROP = CLIENT_UPDATE_PROP;

View File

@ -1,7 +1,25 @@
const { Client } = require("./Client.js");
const { CLIENT_UPDATE_PROP } = require("../IPC.js");
const { Client } = require("../Client.js");
let {addService, addListener} = require("../WebSocket.js");
addListener('disconnect',(global, xclient)=>{
const {intersection, pairs} = xclient.getSucureClients();
for (const [clientid, client] of intersection)
{
client?.send([
{
id: clientid
},
"peer/disconnect"
])
}
for (const [id, peer] of pairs)
{
peer?.pairs.delete(xclient.id);
xclient.pairs.delete(id);
}
});
addService(({
client,
message,
@ -63,7 +81,7 @@ addService(({
message: 'ALREADY-PAIRED'
})
}
if(client.pairs.add(to))
if(client.pairs.has(to))
{
return end({
status: 'fail',

View File

@ -1,4 +1,4 @@
const { Client } = require("./Client.js");
const { Client } = require("../Client.js");
let {randomUUID} = require("crypto");
let {addService,addListener} = require("../WebSocket.js");
const { Room } = require("./Room.js");
@ -27,12 +27,37 @@ addService(({
{
case "pack/to":{
let {to,pack,handshake} = message;
if(!client.packReadable()){
console.error("Okunabilir olmayan client bir mesaj iletiyor")
handshake && end({
type: 'fail'
})
}
if(Client.clients.has(to))
{
let otherPeer = Client.clients.get(to);
if(otherPeer.requiredPair && !otherPeer.pairs.has(to))
if(otherPeer.requiredPair)
{
return handshake && end({
if(!otherPeer.pairs.has(to))
{
console.error("Client, güvenilir olmayan bir cliente mesaj iletiyor")
return handshake && end({
type: 'fail'
})
}
}else{
if(!otherPeer.pairs.has(to))
{
otherPeer.pairs.add(client.id);
client.pairs.add(otherPeer.id);
}
}
if(!otherPeer.packWriteable()){
console.error("Client, yazılabilir olmayan bir cliente mesaj iletiyor")
handshake && end({
type: 'fail'
})
}
@ -44,6 +69,7 @@ addService(({
type: 'success'
})
}else{
console.error("Client, olmayan bir cliente mesaj iletiyor")
handshake && end({
type: 'fail'
})
@ -55,16 +81,25 @@ addService(({
if(Client.clients.has(to))
{
let otherPeer = Client.clients.get(to);
if(otherPeer.requiredPair && !otherPeer.pairs.has(to))
if(otherPeer.requiredPair)
{
return;
if(!otherPeer.pairs.has(to))
{
console.error("Client, güvenilir olmayan bir clientden veri istiyor")
return handshake && end({
type: 'fail'
})
}
}else{
otherPeer.pairs.add(client.id);
client.pairs.add(otherPeer.id);
}
otherPeer.send([{
from: client.id,
pack: pack,
id:messageId
}, 'request']);
};
}else console.error("request/to error not finded Peer");
break;
}
case "response/to":{
@ -74,34 +109,52 @@ addService(({
let otherPeer = Client.clients.get(to);
if(otherPeer.requiredPair && !otherPeer.pairs.has(to))
{
console.error("response istenen peer güvenli değil")
return;
}
otherPeer.send([{
from: client.id,
pack: pack
}, id]);
};
}else console.error("response/to error not finded Peer");
break;
}
case "pack/room":{
let {to,pack, handshake,wom} = message;
if(!client.packReadable()){
console.error("Client paketi okumak için müsait değil")
handshake && end({
type: 'fail'
})
}
if(Room.rooms.has(to))
{
if(!client.rooms.has(to))
{
console.error("Client katılmadığı bir odaya mesaj iletiyor")
return handshake && end({
type: 'fail'
})
};
Room.rooms.get(to).send([{
from: to,
pack: pack,
sender: client.id
}, 'pack/room'], wom ? client.id : void 0);
Room.rooms.get(to).send(
[
{
from: to,
pack: pack,
sender: client.id
}, 'pack/room'
],
wom ? client.id : void 0,
client => client.packWriteable()
);
handshake && end({
type: 'success'
})
}else{
console.error("Olmayan oda için veri gönderme isteniyor")
handshake && end({
type: 'fail'
})

View File

@ -1,4 +1,4 @@
const { Client } = require("./Client");
const { Client } = require("../Client");
let {addService, addListener} = require("../WebSocket.js");
class APNumber{

View File

@ -1,4 +1,4 @@
const { Client } = require("./Client.js");
const { Client } = require("../Client.js");
let {randomUUID,createHash} = require("crypto");
const joi = require("joi");
let {addService,addListener} = require("../WebSocket.js");
@ -161,11 +161,19 @@ Room.fromJSON = function(data, room){
)
return room;
};
Room.prototype.send = function(obj, withOut){
/**
*
* @param {any} obj
* @param {string} withOut
* @param {(client:Client) => boolean} map
*/
Room.prototype.send = function(obj, withOut, map){
for (const client of this.clients.values()) {
if(client.id != withOut)
{
client.send(obj);
(
map ? map(client) : 1
) && client.send(obj);
}
}
termoutput && term.green("Room bulk message ").white(this.name," in ").yellow(this.clients.size + "").white(" clients")('\n');
@ -176,11 +184,18 @@ Room.prototype.send = function(obj, withOut){
Room.prototype.join = function(client){
if(this.notifyActionJoined)
{
this.send([{
id: client.id,
roomid: this.id,
ownerid: this.owner.id
},'room/joined']);
this.send(
[
{
id: client.id,
roomid: this.id,
ownerid: this.owner.id
},
'room/joined'
],
void 0,
client => client.peerInfoNotifiable()
);
};
client.rooms.add(this.id);
this.clients.set(client.id, client);
@ -203,11 +218,18 @@ Room.prototype.down = function(){
Room.prototype.eject = function(client){
if(this.notifyActionEjected)
{
this.send([{
id: client.id,
roomid: this.id,
ownerid: this.owner.id
},'room/ejected']);
this.send(
[
{
id: client.id,
roomid: this.id,
ownerid: this.owner.id
},
'room/ejected'
],
client.id,
client => client.peerInfoNotifiable()
);
}
client.rooms.delete(this.id);
this.clients.delete(client.id);
@ -697,14 +719,20 @@ addService(({
}
room.info.set(name, value);
room.send([
{
name,
value,
roomId:room.id
},
"room/info"
], client.id);
room.send(
[
{
name,
value,
roomId:room.id
},
"room/info"
],
client.id,
client => {
return client.roomInfoNotifiable()
}
);
return end({
status: "success"

View File

@ -0,0 +1,69 @@
let {addService,addListener} = require("../WebSocket.js");
const Stdoutput = [
["notifyPairInfo", true],
["packrecaive", true],
["packsending", true],
["notifyRoomInfo", true]
];
addListener('connect',(global, client)=>{
for (const [name, defaultValue] of Stdoutput)
{
client.store.set(name, defaultValue);
}
client.sync('store');
});
addService(({
client,
end,
global,
message,
next,
response
})=>{
let {type, value} = message;
switch(type)
{
// enable/disable pair/info messages
case 'connection/pairinfo':{
client.store.set("notifyPairInfo", !!value)
client.sync('store');
end(true);
break;
}
case 'connection/roominfo':{
client.store.set("notifyRoomInfo", !!value)
client.sync('store');
end(true);
break;
}
case 'connection/packrecaive':{
client.store.set("packrecaive", !!value)
client.sync('store');
end(true);
break;
}
case 'connection/packsending':{
client.store.set("packsending", !!value)
client.sync('store');
end(true);
break;
}
case 'connection/reset':{
for (const [name, defaultValue] of Stdoutput)
{
client.store.set(name, defaultValue);
}
client.sync('store');
end(true);
break;
}
}
});

View File

@ -3,7 +3,7 @@
let websocket = require("websocket");
let {http} = require("./HTTPServer");
let {randomUUID} = require("crypto");
const { Client } = require("./Services/Client");
const { Client } = require("./Client.js");
const { termoutput } = require("./config");
const { CLIENT_CREATED, CLIENT_DESTROY } = require("./IPC");
const stats = require("./stats");
@ -63,10 +63,18 @@ wsServer.addListener("connect",(socket) => {
stats.mwse_clients--;
emit("disconnect", global, xClient);
CLIENT_DESTROY(id);
Client.clients.set(id, xClient);
Client.clients.delete(id);
clearInterval(timer);
clearInterval(pingTimer);
});
let pingTimer = setInterval(()=> socket.ping('saQut') , 10_000);
socket.addListener("pong",validationText => {
if(validationText.toString('utf8') != "saQut"){
socket.close();
}
})
socket.addListener("message",({type,utf8Data}) => {
stats.ws_recaived_packs++;
stats.ws_total_packs++;

View File

@ -1,15 +1 @@
const {resolve} = require("node:path");
exports.termoutput = false;
exports.HTTP_PORT = 7707;
exports.NETWORK_MASK = '0.0.0.0'; //---> Sadece local için "127.0.0.1" heryerde çalışması için "0.0.0.0"
exports.HTTP_AUTH = true;
exports.HTTP_AUTH_USERNAME = 'saqut';
exports.HTTP_AUTH_USERPASSWORD = 'yum81633';
exports.LOADBALANCE_CPU = false; // ---> Cpu number (2,3,4...) for load balancer process or false for single thread (no load balancer and multiprocess)
exports.IPALLOCATION_DB = resolve(__dirname,"../database/ipallocation.db");
exports.IPALLOCATION_STORE_TYPE = "memory" // or file
exports.termoutput = false;

View File

@ -6,17 +6,17 @@ require("./Services/Auth.js");
require("./Services/Room.js");
require("./Services/DataTransfer.js");
require("./Services/IPPressure.js");
require("./module/IPAllocation.js");
process.on('unhandledRejection',()=>{
require("./Services/Session.js");
process.on('unhandledRejection',(reason, promise)=>{
console.log("Process unhandledRejection",{reason, promise})
});
process.on('rejectionHandled',()=>{
process.on('rejectionHandled',(promise)=>{
console.log("Process rejectionHandled",{promise})
});
process.on('multipleResolves',()=>{
process.on('multipleResolves',(type, promise, value)=>{
console.log("Process multipleResolves",{type, promise, value})
});
process.on('warning',()=>{
process.on('warning',(err)=>{
console.log("Process warning", err)
});

View File

@ -1,224 +0,0 @@
const knex = require("knex");
const { IPALLOCATION_DB } = require("../config");
const crypto = require("node:crypto");
const moment = require("moment");
let IPDB = knex({
client: "sqlite3",
connection: exports.IPALLOCATION_STORE_TYPE == "memory" ? ":memory:" : IPALLOCATION_DB,
useNullAsDefault: true
});
let flovitNumber = [
[
8,
12,
24,
48,
52,
56
],
[
7,
10,
13,
16,
19,
22,
25,
28,
31,
34,
37,
40,
43,
46,
49,
52,
55,
58,
61,
64,
67,
70,
73,
76,
79,
82
]
];
async function read(filter)
{
return await IPDB.table("iptable").where(filter).first();
}
async function has(filter)
{
return (
await IPDB.table("iptable").where(filter).count('* as key').first()
).key != 0;
}
async function flushAll()
{
await IPDB.table("iptable").delete();
}
async function write(value,filter)
{
await IPDB.table("iptable").where(filter).update(value);
}
async function create(value)
{
await IPDB.table("iptable").insert(value);
}
async function remove(...filterArgs)
{
await IPDB.table("iptable").where(...filterArgs).delete();
}
async function migrate()
{
if(!await IPDB.schema.hasTable("iptable"))
{
await IPDB.schema.createTable("iptable",function(table){
table.bigInteger("ipaddress").unsigned().primary();
table.text("owner").unique();
table.string("type", 50).defaultTo("client");
table.dateTime("created").defaultTo(IPDB.fn.now());
});
}
};
function iptobin(a,b,c,d)
{
const bin =
((a | 0) << 24) |
((b | 0) << 16) |
((c | 0) << 8) |
(d | 0);
return bin;
}
function bintoip(bin)
{
if(typeof bin == "string")
{
bin = bin | 0;
}
const octet1 = (bin >>> 24) & 0xff;
const octet2 = (bin >>> 16) & 0xff;
const octet3 = (bin >>> 8) & 0xff;
const octet4 = bin & 0xff;
return [octet1,octet2,octet3,octet4];
}
function randomMaskINIT()
{
if(typeof bin == "string")
{
bin = bin.split('.').map(e => e | 0);
}
let globalmask = crypto.randomInt(flovitNumber[0].length);
let macromask = crypto.randomInt(flovitNumber[1].length);
return [
flovitNumber[0][globalmask],
flovitNumber[1][macromask],
0,
0
]
}
async function init()
{
await migrate();
await flushAll();
await create({
ipaddress: 0, // globalmask & macromask
owner: randomMaskINIT().join('.'),
type: "system",
created: moment().format()
});
hook()
}
init.inited = false;
async function getip(owner, type)
{
let call = await read({
owner,
type
});
return !call ? false : {
ipaddress: call.ipaddress,
date: call.created
};
}
async function setip(owner, type, ip)
{
await getip(owner, type) ?
await write({
ipaddress: ip
},{
owner,
type
})
:
await create({
ipaddress: ip,
owner,
type
});
}
async function allocateIP(owner, type, changeOverlord)
{
let [a,b,c,d] = [ ,,, ];
if(changeOverlord)
{
[a,b,c,d] = randomMaskINIT();
}else{
let ipinfo = await read({
ipaddress: 0,
type: "system"
});
[a,b,c,d] = ipinfo.owner.split('.').map(e => e | 0);
}
while(1)
{
if(d != 255)
{
d++;
}else if(c != 255){
c++;
}else if(b != 255){
b++;
}else if(a != 255){
a++;
}else{
[a,b,c,d] = randomMaskINIT();
}
let query = {
ipaddress: iptobin(a,b,c,d)
};
let result = await has(query);
if(result == false)
{
await create({
ipaddress: iptobin(a,b,c,d),
owner,
type
});
await write({
owner: [a,b,c,d].join('.')
},{
type: "system",
ipaddress: 0
});
break;
}else{
console.log("Checking",[a,b,c,d].join('.'))
}
}
return[a,b,c,d].join('.')
}
process.nextTick(init);

508
console/Hemex.js Normal file
View File

@ -0,0 +1,508 @@
function Hemex()
{
};
Hemex.EOL = "\n";
/**
* Hemex variable white space chars
* @type {Number[]}
*/
Hemex.WhiteSpace = [
9,10,11,12,13,32,133
];
/**
* Current cursor position
* @type {Number}
*/
Hemex.prototype.offset = 0;
/**
* Mapping offset points
* @type {Number[]}
*/
Hemex.prototype.offsetMap = [];
Hemex.prototype.beginPosition = function(){
this.offsetMap.push(
this.getLastPosition()
)
}
/**
* Adding current position to offset map
*/
Hemex.prototype.acceptPosition = function(){
let t = this.offsetMap.pop();
this.setLastPosition(t)
}
/**
* Get text range current and parent offsets
* @returns {[Number,Number]}
*/
Hemex.prototype.positionRange = function(){
let len = this.offsetMap.length;
if(len == 0)
{
return [0,this.offset]
}else if(len == 1){
return [this.offset,this.offsetMap[len - 1]]
}else{
return [this.offsetMap[len - 2],this.offsetMap[len - 1]]
}
}
/**
* Get text range between current offset and parent offset
* @returns {String}
*/
Hemex.prototype.getPositionRange = function(){
let u = this.positionRange();
return this.text.slice(u[0],u[1])
}
/**
* Cancel current position and return to parent offset
*/
Hemex.prototype.rejectPosition = function(){
this.offsetMap.pop()
}
/**
* Get current layer of position from last offset of map
* @returns {Number}
*/
Hemex.prototype.getLastPosition = function(){
return this.offsetMap.length == 0 ? this.offset : this.offsetMap.slice(-1)[0]
}
/**
* Set last position offset from offset map last layer
* @param {Number} n
*/
Hemex.prototype.setLastPosition = function(n){
if(this.offsetMap.length == 0)
this.offset = n
else this.offsetMap[this.offsetMap.length - 1] = n
}
/**
* Get current layer of position from last offset of map
* Some as getLastPosition()
* @returns {Number}
*/
Hemex.prototype.getOffset = function(){
return this.getLastPosition()
}
/**
* Set last position offset from offset map last layer and return it value
* @param {Number} n
* @returns {Number}
*/
Hemex.prototype.setOffset = function(n){
this.setLastPosition(n);
return this.getLastPosition()
}
/**
* Get text length
* @type {Number}
*/
Hemex.prototype.length = 0;
/**
* Hemex lexing data
* @type {String}
*/
Hemex.prototype.text = "";
/**
* set lexing data
* @param {String} text
* @returns {void}
*/
Hemex.prototype.setText = function(text){
this.offset = 0;
this.length = text.length;
this.offsetMap = [];
this.text = text;
}
/**
* get lexing all data
* @returns {String}
*/
Hemex.prototype.getText = function(){
return this.text;
}
/**
* Get one character from cursor
* @param {Number} n
* @returns {String}
*/
Hemex.prototype.getChar = function(n){
return this.text.charAt(n?this.getOffset()+n:this.getOffset())
}
/**
* Boolean
* @param {Number} n
* @returns {String}
*/
Hemex.prototype.isChar = function(b){
return this.getChar() == b
}
/**
* Dump all data from cursor position to end of char
* @param {Number} n
*/
Hemex.prototype.dump = function(n){
return this.text.slice(this.getOffset(),this.getOffset()+n)
}
/**
* Control coming end of line
* @returns {Bollean}
*/
Hemex.prototype.isEnd = function(){
return this.length > this.getOffset()
}
/**
* Forward one char
*/
Hemex.prototype.nextChar = function(){
this.setOffset(this.getOffset() + 1);
}
/**
* Forward n char
*/
Hemex.prototype.toChar = function(n){
this.setOffset(this.getOffset() + n);
}
/**
* Reading while end of line
* @returns {String}
*/
Hemex.prototype.getLine = function(){
return this.of(function(){
switch(this.getChar())
{
case Hemex.EOL: return false;
default: return true;
}
}.bind(this))
}
/**
* Read all data until the function returns false
* @param {Boolean} p
* @param {(char:String)=>Boolean} e
* @returns {String}
*/
Hemex.prototype.of = function(e,p){
let k = [],count=0;
while(this.isEnd()){
if(e(p,count)) k.push(this.getChar());
else return k.join('');
count++;
this.nextChar();
};
return k.length == 0 ? false : k.join('')
}
Hemex.prototype.each = function(e,p){
let k = [];
while(this.isEnd())
if(!e(p)) return;
else this.nextChar();
}
Hemex.prototype.while = function(e,p){
let k = [];
while(this.isEnd())
if(!e(p)) return;
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isNumber = function(reverse){
let c = this.getChar().charCodeAt(0);
let result = c >= 48 && c <= 57;
return reverse ? !result : result;
}
/**
* Read all data until char type is not number
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.readNumbers = function(reverse){
return this.of(this.isNumber.bind(this),reverse)
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isBigLetter = function(reverse){
let c = this.getChar().charCodeAt(0);
let result = c >= 97 && c <= 122;
return reverse ? !result : result;
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isSmallLetter = function(reverse){
let c = this.getChar().charCodeAt(0);
let result = c >= 65 && c <= 90;
return reverse ? !result : result;
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isLetter = function(reverse){
let result = this.isSmallLetter() || this.isBigLetter()
return reverse ? !result : result;
}
/**
* Read all data until char type is not letter
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.readLetters = function(reverse){
return this.of(this.isLetter.bind(this),reverse)
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isWhiteSpace = function(reverse){
let c = this.getChar(),ct = c.charCodeAt(0);
let result = (
c == '\n' ||
c == '\r' ||
c == '\t' ||
c == ' ' ||
Hemex.WhiteSpace.includes(ct)
)
return reverse ? !result : result;
}
/**
* Read all data until char type is not white space
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.readWhiteSpace = function(reverse){
return this.of(this.isWhiteSpace.bind(this),reverse)
}
/**
* Controlling data
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.include = function(words,next){
this.beginPosition();
for(let i = 0; i<words.length; i++)
{
if(words[i] != this.getChar())
{
this.rejectPosition();
return false;
};
this.nextChar();
};
if(next) this.acceptPosition();
else this.rejectPosition();
return true;
}
/**
* Controlling data
* @param {Boolean} reverse
* @returns {String|boolean}
*/
Hemex.prototype.includes = function(arrays,accept){
this.beginPosition();
let flags = Array.from(arrays).fill(true);
let index = 0;
this.each(function(){
let stopLoop = true;
for(let T in arrays)
{
if(!flags[T] || arrays[T].length <= index) continue;
stopLoop = false;
flags[T] &= arrays[T][index] == this.getChar()
};
index++;
return !stopLoop && flags.filter(function(val){return val}).length != 0;
}.bind(this));
let result = arrays.filter(function(_,index){return flags[index]});
if(accept) this.acceptPosition();
else this.rejectPosition();
return result.length == 0 ? false : result
}
/**
* Parsing number formats like; 12 75.1 0xE7 0b10 +3.46
* @returns {[String,Number]}
*/
Hemex.prototype.readNumber = function(){
let data = [];
let base = 10;
let nextDot = false;
let c = this.getChar();
if(this.isChar('0'))
{
this.nextChar();
switch(this.getChar())
{
case 'x':{
base = 16;
this.nextChar();
data.push('0x')
break;
}
case 'b':{
base = 2;
this.nextChar();
data.push('0b')
break;
}
default:{
base = 8;
this.nextChar();
data.push('0')
}
}
}else base = 10;
this.each(()=>{
switch(c = this.getChar())
{
case '0':
case '1':{
data.push(c);
break;
}
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':{
if(base >= 8){
data.push(c);
break;
}else return false;
}
case '8':
case '9':{
if(base >= 10){
data.push(c);
break;
}else return false;
}
case 'A':
case 'a':
case 'B':
case 'b':
case 'C':
case 'c':
case 'D':
case 'd':
/* case 'E': case 'e': */
case 'F':
case 'f':{
if(base >= 16){
data.push(c);
break;
}else return false;
}
case '.':{
if(!nextDot){
if(data.length == 0){
data.push("0");
}else data.push(".");
nextDot = true;
isFloat = true;
}else{
throw new Error("Float number in Double dot");
};
break;
}
case 'E':
case 'e':{
if(this.getChar(1)!='+'){
if(base == 16){
data.push(c);
break;
}else return false;
};
if(data.length == 0){
this.rejectPosition();
return false;
};
data.push('e');
this.nextChar();
if(this.getChar()=='+' || this.getChar()=='-'){
data.push(char());
this.nextChar();
};
let result = null;
this.each(()=>{
switch(this.getChar()){
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':{
data.push(this.getChar());
this.nextChar();
break;
}
default:{
return false;
}
}
})
}
default:{
return false;
}
};
return true
});
return data.length == 0 ? false : [data.join(''),base]
}
Hemex.prototype.syntaxs = new Map();
/**
*
* @param {string} name
* @param {(hmx:Hemex, result: (result:any) => any,...args:any[]) => any} callback
*/
Hemex.prototype.syntax = function(name, callback){
this.syntaxs.set(name, callback)
}
Hemex.prototype.give = function(name, ...args){
let sandbox = this.syntaxs.get(name);
if(sandbox)
{
let res = undefined;
hmx.beginPosition();
if(sandbox(
this,
arg => {
res = arg
},
...args
))
{
hmx.acceptPosition();
return res;
}else{
hmx.rejectPosition();
return res;
}
}
}
/**
* @param {Error} message
*/
Hemex.prototype.throw = function(message){
throw new Error(message)
};

21
console/index.html Normal file
View File

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<base href="/console/">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket Terminal</title>
<style>
body{
background-color: black;
}
</style>
</head>
<body>
<div id="terminal"></div>
<script src="xterm.js"></script>
<link rel="stylesheet" href="xterm.min.css">
<script src="./Hemex.js"></script>
<script src="lib.js"></script>
</body>
</html>

908
console/lib.js Normal file
View File

@ -0,0 +1,908 @@
var term = new Terminal({
cursorStyle: "underline",
cols: 180,
rows: 50
});
term.open(document.querySelector('#terminal'));
function newLine(t = true, prompt = true) {
t && term.write('\r\n');
cursorPosition = 0;
if(prompt)
{
term.write(DEFAULT_PROMPT);
cursorPosition += CLEAR_STYLING(DEFAULT_PROMPT).length;
}
resetUserSpace()
}
let waitms = ms => new Promise(ok => setTimeout(()=>ok(), ms));
let cursorPosition = 0;
let _buffer = [];
let COLOR_RESET = () => "\x1B[0m";
let COLOR_TEXT = (r,g,b) => `\x1B[38;2;${r};${g};${b}m`;
let COLOR_BACKGROUND = (r,g,b) => `\x1B[48;2;${r};${g};${b}m`;
let CURSOR_MOVE = (row, col) => `\x1B[${row};${col}H`;
let CURSOR_MOVE_UP = () => `\x1B[A`;
let CURSOR_MOVE_DOWN = () => `\x1B[B`;
let CURSOR_MOVE_RIGHT = () => `\x1B[C`;
let CURSOR_MOVE_LEFT = () => `\x1B[D`;
let CLEAR_SCREEN = () => `\x1B[2J`;
let CLEAR_LINE = () => `\x1B[2K`;
term.write(CURSOR_MOVE(2,1));
let CLEAR_STYLING = (text) => text.toString().replace(/\x1B(.*?)m/ig,'');
function writeHi()
{
term.write(
"Merhaba, "
+ COLOR_TEXT(0,255,0)
+ COLOR_BACKGROUND(32,32,32)
+ ' saQut RC Shell '
+ COLOR_RESET()
+ "\n\n\r"
);
newLine();
}
let userSpace = {
buffer: [],
paddingChars: 0,
size: 0
};
function resetUserSpace()
{
userSpace = {
buffer: [],
paddingChars: 0,
size: 0
}
}
function getCurrentLine()
{
return term
.buffer
.active
.getLine(
term
.buffer
.active
.cursorY
)
.translateToString()
}
let pausedInput = false;
let activeProgram = false;
term.onData(async chars => {
if(pausedInput)
{
return
}
if(chars.length > 1)
{
return await DefaultProcess(chars);
}
if(chars.length == 1)
{
if(activeProgram)
{
if(activeProgram._sys.idleChar)
{
return activeProgram._sys.idleChar.triggerChar(chars);
}
}
return await DefaultProcess(chars);
}else if(activeProgram){
return
}
let tokens = [];
chars.toString().replace(/(\x1B.+?m)|(\x1B\[.+)|(.+)/igm, (_,a,b,c,d,e,f,g) => {
let charx = a || b || c;
if(charx)
{
if(/^(\x1B.*?m)$|^(\x1B\[.*)$/i.test(charx))
{
tokens.push([charx,])
}else{
tokens.push([,charx])
}
}
});
for (const [code, chars] of tokens)
{
pausedInput = true;
if(code)
{
await DefaultProcess(code);
}else if(chars)
{
for (const char of chars.split(''))
{
await DefaultProcess(char);
}
}
pausedInput = false;
}
})
async function DefaultProcess(char)
{
let contextchanged = false;
switch(char)
{
// Backspace
case '\x7f':
if(userSpace.paddingChars > 0)
{
term.write('\b \b');
cursorPosition--;
let after = userSpace.buffer.slice(0, userSpace.paddingChars - 1);
let before = userSpace.buffer.slice(userSpace.paddingChars);
userSpace.buffer = [
...after,
...before
];
userSpace.paddingChars--;
userSpace.size--;
contextchanged = true;
term.write(before.join('')+" \b");
term.write(before.map(() => '\b').join(''));
}
break;
// Enter
case '\r':
let command = userSpace.buffer.join('');
if(activeProgram && activeProgram._sys.idleReadline)
{
newLine(true, false);
return activeProgram._sys.triggerReadline(command);
}
let parsedCommand = ParseCommand(command);
if(parsedCommand != false)
{
let program = parsedCommand.find(e => e.type == 'commandname').data;
pausedInput = true;
newLine(true, false);
await ExecuteCommand(
program,
parsedCommand
);
pausedInput = false;
newLine(false);
}else{
newLine();
}
break;
case CURSOR_MOVE_LEFT():
if(userSpace.paddingChars > 0)
{
userSpace.paddingChars--;
term.write(CURSOR_MOVE_LEFT());
}
break;
case CURSOR_MOVE_UP():
case CURSOR_MOVE_DOWN():
break;
// Delete
case "\x1B[3~":
let after = userSpace.buffer.slice(0, userSpace.paddingChars);
let before = userSpace.buffer.slice(userSpace.paddingChars + 1);
term.write(before.join('')+" \b");
term.write(before.map(() => '\b').join(''));
userSpace.buffer = [
...after,
...before
];
userSpace.size--;
contextchanged = true;
break;
case CURSOR_MOVE_RIGHT():
if(userSpace.paddingChars < userSpace.size)
{
term.write(CURSOR_MOVE_RIGHT());
userSpace.paddingChars++;
}
break;
default:
let printable = char.charCodeAt(0);
if(printable < 32)
{
return
}
// insert or append ?
if(userSpace.paddingChars == userSpace.size)
{
term.write(char);
userSpace.buffer = [
...userSpace.buffer.slice(0, userSpace.paddingChars),
char,
...userSpace.buffer.slice(userSpace.paddingChars),
];
userSpace.paddingChars++;
userSpace.size++;
cursorPosition++;
contextchanged = true;
}else{
let after = userSpace.buffer.slice(0, userSpace.paddingChars);
let before = userSpace.buffer.slice(userSpace.paddingChars);
userSpace.buffer = [
...after,
char,
...before
];
term.write(char);
term.write(before.join(''));
term.write(before.map(() => '\b').join(''));
contextchanged = true;
userSpace.paddingChars++;
userSpace.size++;
cursorPosition++;
}
}
if(!activeProgram)
{
contextchanged && COLORIZE_LINE();
}
};
async function COLORIZE_LINE()
{
let before = userSpace.buffer.slice(0, userSpace.paddingChars);
let after = userSpace.buffer.slice(userSpace.paddingChars);
let coloredCommand = CommandColorize(before.concat(after).join(''));
if(coloredCommand)
{
// Cursoru en başa al
term.write(CURSOR_MOVE_LEFT().repeat(before.length));
term.write(coloredCommand);
term.write(CURSOR_MOVE_LEFT().repeat(after.length));
}
}
let USER_SPACE = "virtualhost";
let USER_NAME = [
147 + Math.random() * 25 | 0,
200 + Math.random() * 56 | 0,
0 + Math.random() * 256 | 0,
0 + Math.random() * 256 | 0
].join('.');
let DEFAULT_PROMPT = (
COLOR_RESET()
+ '['
+ COLOR_TEXT(0,255,0)
+ USER_SPACE
+ ' '
+ COLOR_TEXT(0,128,255)
+ USER_NAME
+ COLOR_RESET()
+ '] $ '
);
writeHi();
let _color_command = [255,255,255];
let _color_option = [128,128,255];
let _color_stringdata = [128,255,64];
function CommandColorize(command)
{
let hmx = new Hemex();
hmx.setText(command);
let output = [];
let commandBefore = hmx.of(() => !hmx.isLetter());
if(commandBefore.length != 0)
{
output.push(
COLOR_TEXT(255,255,255),
commandBefore,
COLOR_RESET()
)
}
if(!hmx.isLetter())
{
return false
}
let commandName = hmx.of(() => hmx.isLetter() || hmx.isNumber() || hmx.includes(['-','_',':','.','$']));
if(hmx.isEnd() && !hmx.isWhiteSpace())
{
return false
}
output.push(
COLOR_TEXT(..._color_command),
commandName,
COLOR_RESET()
);
let all = [], stringstarted = false;
hmx.each(() => {
let char = hmx.getChar();
switch(char)
{
case '\'':
case '"':
case '`':{
if(stringstarted == false)
{
stringstarted = char;
all.push(COLOR_TEXT(..._color_stringdata), char)
}else if(stringstarted == char){
stringstarted = false;
all.push(char, COLOR_TEXT(..._color_option))
}else{
all.push(char)
}
return true;
}
default:{
if(all.length == 0)
{
all.push(COLOR_TEXT(..._color_option), char);
}else{
all.push(char)
}
return true;
}
}
});
output.push(all.join(''))
output.push(COLOR_RESET())
return output.join('')
}
function ParseCommand(command)
{
let hmx = new Hemex();
hmx.setText(command);
hmx.of(() => !hmx.isLetter());
if(!hmx.isLetter())
{
return false
}
let onlyArgs = false;
let commandName = hmx.of(() => hmx.isLetter() || hmx.isNumber() || hmx.includes(['-','_',':','.','$']));
if(hmx.isEnd() && !hmx.isWhiteSpace())
{
return false
}
hmx.readWhiteSpace();
let args = [];
let xargs = [], operation = 'option';
hmx.beginPosition();
hmx.while(()=>{
if(hmx.include('--') && operation == 'option')
{
xargs.length && (
args.push({
type: 'data',
data: xargs.join('')
}),
xargs = []
);
hmx.beginPosition();
hmx.include('--', true);
let argumentkey = hmx.of(() => hmx.isLetter() || hmx.isNumber() || hmx.includes(['-','_',':','.','$']));
if(argumentkey == false)
{
hmx.rejectPosition();
return true;
}
args.push({
type: "argument",
data: argumentkey
});
hmx.acceptPosition();
hmx.readWhiteSpace();
return true;
}
if(hmx.include('-') && operation == 'option')
{
xargs.length && (
args.push({
type: 'data',
data: xargs.join('').trim()
}),
xargs = []
);
hmx.beginPosition();
hmx.nextChar();
let argumentkey = hmx.of(() => hmx.isLetter() || hmx.isNumber());
if(argumentkey == false)
{
hmx.rejectPosition();
return true;
}
argumentkey.split('').forEach(e => {
args.push({
type: "flag",
data: e
})
})
hmx.acceptPosition();
hmx.readWhiteSpace();
return true;
}
let char;
if((char = hmx.includes(['\'','"'])) && operation == 'option')
{
xargs.length && (
args.push({
type: 'data',
data: xargs.join('')
}),
xargs = []
);
let escapeChar = char[0];
hmx.nextChar();
let data = [];
hmx.while(() => {
let char = hmx.getChar();
switch(hmx.getChar())
{
case escapeChar:{
hmx.nextChar();
return false;
}
default:{
data.push(char);
hmx.nextChar();
return true;
}
}
});
args.push({
type: "string",
data: data.join('')
})
hmx.readWhiteSpace();
return true;
}
xargs.push(hmx.getChar());
if(hmx.isWhiteSpace())
{
operation = 'option';
}else{
operation = 'data';
}
hmx.nextChar();
if(hmx.isEnd())
{
return true;
}else{
args.push({
type: "data",
data: xargs.join('')
})
return false;
}
});
onlyArgs = hmx.getPositionRange();
hmx.acceptPosition();
return [{
type: "cmdline",
data: command
},{
type: "commandname",
data: commandName
},{
type: "arguments",
data: onlyArgs
}, ...args]
}
function generatepipe()
{
pausedInput = true;
let events = {};
let gen = {
stdin: {
setPaused(boolean){
pausedInput = Boolean(boolean);
},
async readline(){
gen.stdin.setPaused(false);
gen._sys.idleReadline = true;
return await new Promise(ok => {
events['readline'] = (result) => {
gen.stdin.setPaused(true);
gen._sys.idleReadline = false;
ok(result)
events['readline'] = null;
};
})
},
async getchar(){
gen.stdin.setPaused(false);
gen._sys.idleChar = true;
return await new Promise(ok => {
events['read'] = (result) => {
gen.stdin.setPaused(true);
gen._sys.idleChar = false;
events['read'] = null;
ok(result)
};
})
}
},
stdout: {
async write(...args){
term.write(args.map(e => e.toString()).join(' '))
},
async writeln(...args){
term.writeln(args.map(e => e.toString()).join(' '))
}
},
stderr: {
async write(...args){
term.write(COLOR_TEXT(255,0,0));
term.write(args.map(e => e.toString()).join(' '))
term.write(COLOR_RESET());
},
async writeln(...args){
term.write(COLOR_TEXT(255,0,0));
term.writeln(args.map(e => e.toString()).join(' '))
term.write(COLOR_RESET());
}
},
_sys:{
idleReadline: false,
idleChar: false,
triggerReadline(text){
if(events['readline']) events['readline'](text)
},
triggerChar(text){
if(events['read']) events['read'](text)
},
destroy(){
gen.stdin.setPaused(false);
events = void 0;
gen._sys.idleReadline = false;
gen._sys.idleChar = false;
gen._sys = void 0;
gen.stderr = void 0;
gen.stdin = void 0;
gen.stdout = void 0;
activeProgram = false;
}
}
};
activeProgram = gen;
return gen;
}
async function ExecuteCommand(command, cmdline)
{
let Namespace = await CommandNamespace(command);
if(Namespace == null)
{
return
}
try
{
await new Promise(async (ok,reject) => {
resetUserSpace();
let program = new Namespace(generatepipe());
program.exit = () => ok();
try{
let t = program.main(cmdline);
if(t?.constructor?.name == 'AsyncFunction')
{
await t;
}
}catch(e){
reject(e)
}
})
}
catch(exception)
{
term.writeln('');
term.write(COLOR_TEXT(255,0,0));
term.writeln('Yazılım hatası:');
term.writeln('=========================');
term.write(COLOR_RESET());
term.writeln(exception.message);
term.writeln(exception.stack.replace(/\n/g,'\r\n'));
term.write(COLOR_TEXT(255,0,0));
term.writeln('=========================');
term.write(COLOR_RESET());
}
finally
{
pausedInput = false;
activeProgram = void 0;
}
}
async function InspectRepo()
{
if(CommandNamespace.repos.find(e => e.downloaded == false))
{
term.writeln(COLOR_RESET()+"\r\nUpdating repos....")
await wait(100);
}else{
term.writeln('');
}
for(const repo of CommandNamespace.repos)
{
if(repo.downloaded == false)
{
term.writeln(`Downloading:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
await wait(100);
let file = await DownloadRepoMetapack(repo.endpoint);
repo.packages = file;
term.writeln(`Downloaded:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
await wait(100);
repo.downloaded = true;
}
}
return
}
async function CommandNamespace(command)
{
let package = false;
if(!CommandNamespace.namespaces.has(command))
{
await InspectRepo();
for(const repo of CommandNamespace.repos)
{
for (const repopackage in repo.packages)
{
let packageName = repopackage;
let meta = repo.packages[repopackage];
let _command = meta.commands.find(_command => command == _command);
if(_command)
{
package = [packageName,meta];
}
}
}
if(package == false)
{
term.writeln(COLOR_TEXT(255,0,0) + "'" + command + "' not found !" + COLOR_RESET());
}else{
term.writeln([
COLOR_TEXT(0,255,0),
package[0],
COLOR_TEXT(200,200,200),
" paketine ait olan ",
COLOR_TEXT(0,255,0),
command,
COLOR_TEXT(200,200,200),
" komutunu kullanabilmek için aşağıdaki komutunu çalıştırın"
].join('') +
"\r\n\n" +
CommandColorize(`load '${package[0]}' from 'official'`)+
"\n"
);
}
}else{
return CommandNamespace.namespaces.get(command);
}
}
function wait(ms)
{
return new Promise(ok => setTimeout(() => ok(),ms));
}
async function DownloadRepoMetapack(endpoint)
{
let request = await fetch(endpoint,{
method: "get",
cache: "force-cache",
priority: "high",
redirect: "follow",
referrerPolicy: "no-referrer"
});
await wait(100);
try{
let response = await request.json();
return response;
}catch
{
return null
}
}
CommandNamespace.namespaces = new Map();
CommandNamespace.repos = [{
name: "official",
endpoint: new URL('/console/official/packages.json',window.location),
downloaded: false,
packages: {}
}];
class SystemLoad{
stdin = null;
stdout = null;
constructor(pipe){
this.stdin = pipe.stdin;
this.stdout = pipe.stdout;
}
printhelp()
{
this.stdout.writeln('Kullanım şekli: ');
this.stdout.writeln('ExampleProgram paketini kurmak için aşağıdaki komutu yazmalısınız\r\n');
this.stdout.writeln(
CommandColorize(`load 'ExampleProgram'`)+"\r\n"
);
}
async main(args)
{
let m = false, nargs = args.filter(({type}) => {
if(type == "arguments") return m = true, false;
return m;
});
let packname;
if(nargs[0].type == "string")
{
packname = nargs[0].data;
}else{
this.printhelp();
this.exit();
return;
}
if(CommandNamespace.repos.find(e => e.downloaded == false))
{
this.stdout.writeln(COLOR_RESET()+"\r\nUpdating repos....")
}else{
this.stdout.writeln('');
}
for(const repo of CommandNamespace.repos)
{
if(repo.downloaded == false)
{
this.stdout.writeln(`Downloading:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
await wait(100);
let file = await DownloadRepoMetapack(repo.endpoint.href);
if(file == null){
continue
}
repo.packages = file;
this.stdout.writeln(`Downloaded:${COLOR_TEXT(0,255,0)} ${repo.name} ${COLOR_RESET()} ${repo.endpoint}`);
await wait(100);
repo.downloaded = true;
}
}
let endpoints = CommandNamespace.repos.map(e => e.packages[packname] && e.packages[packname].endpoints.map(endpoint => new URL(endpoint,e.endpoint))).filter(e => !!e)
if(endpoints.length == 0)
{
term.writeln(COLOR_TEXT(255,0,0) + "'" + packname + "' repo not found !" + COLOR_RESET());
await wait(100);
this.exit();
return;
}
for (const endpoint of endpoints[0])
{
let href = endpoint.href;
this.stdout.writeln(`Packet downloading :${COLOR_TEXT(0,255,0)} ${packname} ${COLOR_RESET()} ${endpoint.href}`);
await wait(100);
let request = await fetch(href,{
method: "get",
cache: "force-cache",
priority: "high",
redirect: "follow",
referrerPolicy: "no-referrer"
});
this.stdout.writeln(`Packet unzip to :${COLOR_TEXT(0,255,0)} /mount/xpack/${packname}/ ${COLOR_RESET()}`);
await wait(100);
let script = await request.text();
try{
this.stdout.writeln(`Analyzing..`);
await new Promise(ok => setTimeout(() => ok(),1000));
(new Function(script))();
this.stdout.writeln(`Process success`);
await wait(100);
}catch{
this.stderr.writeln(`Process error`);
await wait(100);
}
}
this.exit();
}
};
CommandNamespace.namespaces.set('load', SystemLoad);

View File

@ -0,0 +1,51 @@
class CommandJS{
stdin = null;
stdout = null;
stderr = null;
constructor(pipe){
this.stdin = pipe.stdin;
this.stdout = pipe.stdout;
this.stderr = pipe.stderr;
}
async main(args)
{
let commandline = args.find(e => e.type == "arguments");
if(commandline)
{
try{
let t = eval(commandline.data);
if(typeof t != "undefined")
{
this.stdout.writeln(JSON.stringify(t,null, ' ').replace(/\n/g,'\r\n'));
}
}catch(e){
this.stderr.writeln(e.message);
}
}
this.exit();
}
};
class CommandJSRaw{
stdin = null;
stdout = null;
stderr = null;
constructor(pipe){
this.stdin = pipe.stdin;
this.stdout = pipe.stdout;
this.stderr = pipe.stderr;
}
async main(args)
{
let commandline = args.find(e => e.type == "arguments");
if(commandline)
{
let t = eval(commandline.data);
this.stdout.writeln(t);
}
this.exit();
}
};
CommandNamespace.namespaces.set('js', CommandJS);
CommandNamespace.namespaces.set('jsecho', CommandJSRaw);

View File

@ -0,0 +1,18 @@
class CommandSet{
stdin = null;
stdout = null;
stderr = null;
constructor(pipe){
this.stdin = pipe.stdin;
this.stdout = pipe.stdout;
this.stderr = pipe.stderr;
}
async main(args)
{
this.stdout.writeln(JSON.stringify(args,null, ' ').replace(/\n/g,'\r\n'));
this.exit();
}
};
CommandNamespace.namespaces.set('set', CommandSet);

View File

@ -0,0 +1,13 @@
{
"native": {
"endpoints": [
"./native/js.js",
"./native/set.js"
],
"commands": [
"js",
"jsecho",
"set"
]
}
}

18
console/official/plan.txt Normal file
View File

@ -0,0 +1,18 @@
Oluşturulmuş cihazlar | device list
Yeni AudioContext | device create audio /dev/audio0
Yeni HTMLVideoElement | device create video /dev/video0
getUserMedia isteği | webcam 480x360 --audio /dev/audio0 --video /dev/video0
AudioContext sesi dışarı verme | device /dev/audio0 --output speaker
AudioContext sesi açma | device /dev/audio0 --value 100
Videoyu oynatma | lightplayer --source /dev/video0 --target popup
Tüm bağlantıları görme | rtc connections
RTC Oluşturma | rtc create "peer-0"
RTC offer verme | rtc "peer-0" create offer
RTC offer alma | rtc "peer-0" emit offer "offer message"
RTC answer verme | rtc "peer-0" create answer
RTC answer alma | rtc "peer-0" emit answer "answer message"
RTC answer verme | rtc "peer-0" create answer "answer message"
RTC candidate alma | rtc "peer-0" getcandidate --all
RTC candidate verme | rtc "peer-0" setcandidate

2
console/xterm.js Normal file

File diff suppressed because one or more lines are too long

1
console/xterm.min.css vendored Normal file
View File

@ -0,0 +1 @@
.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:0}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm .xterm-cursor-pointer,.xterm.xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}

2
console/xterm.min.js vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -1,93 +0,0 @@
# Namespace
## Auth
### auth/public:
- Eşleşme gerekmez, herkese açık hale getirir
- İşlem bittiğinde success değeri verirr
### auth/private
Eşleşme gerekir, biri size mesaj göndermek için onay alması gerekir
Onaylandığında success gönder
### request/pair
- private bir eşe onay isteği gönderirsiniz, eşe bildirim gider
- Client online değilse fail CLIENT-NOT-FOUND mesajı döner
- Zaten kabul etmişse success ALREADY-PAIRED mesajı döner
- Zaten onay isteği göndermişseniz fail ALREADY-REQUESTED mesajı döner
- Gönderildiğinde success REQUESTED mesajı döner
- Eş isteğe herhangi bir cevap dönerse request/pair veya rejected/pair tetiklenir ve from değerleriyle kim oldukları bildirilir
### accept/pair
- Uzak eşin onay isteğine onay verirsiniz
### accept/pair
- Uzak eşin onay isteğine onay verirsiniz
### reject/pair
- Uzak eşin isteğini reddedersiniz
### pair/list
- Erişebileceğiniz private eşlerin ve sizinle etkileşime giren eşlerin
listesini verir
### auth/login
- saQut Account hesabıyla ilgili ön tanımlı hızlı işlem yapar
### auth/check
- saQut Account hesabıyla ilgili ön tanımlı hızlı işlem yapar
### auth/logout
- saQut Account hesabıyla ilgili ön tanımlı hızlı işlem yapar
## Room
### myroom-info
Kendinize özel açılan odanın bilgisini verir
### room-peers
İçerisinde bulunduğunuz odanın içerisinde bulunan eşlerin listesini verir
### room-info
İçerisinde bulunduğunuz odanın bilgisini verir
### joinedrooms
Katıldığınız odaların listesini verir
### closeroom
Sahibi olduğunuz odayı kapatır
### create-room
Yeni bir oda oluşturur
### joinroom
Bir odaya katılır veya sahibine katılma isteği gönderirsiniz
### accept/invite-room
Gizli bir odaya katılma isteği gödneren kişiyi odaya kabul edersiniz
### reject/invite-room
Gizli bir odaya katılma isteği gödneren kişiyi reddedersiniz
### room/list
Herkese açık olan tüm odaları listeler
## Data transfer
### pack/to
Diğer eşe bir paket gönderir
### pack/room
Bir odaya katılmış tüm üyelere, oda üzerinden bir mesaj gönderir

View File

@ -1,91 +0,0 @@
## Kurulum
### Proje ortamına kurulumu
```html
<script src="https://ws.saqut.com/script"></script>
```
### Geliştirme ortamına kurulumu
```javascript
const wsjs = new MWSE({
endpoint: "https://ws.saqut.com/" // MSWS kurulu sunucu adresi
});
wsjs.scope(async () => {
// Bağlantı sağlandığında burası tetiklenir
})
```
### Kendi bağlantı kimliğini öğrenme
```javascript
wsjs.scope(async () => {
let me = wsjs.peer('me'); // Kendi bağlantınız üzerinde işlem yaparken `me` olarak bahsedersiniz
console.log(me.socketId); // Her eşin tekil bir socketIdsi vardır
})
```
### Sanal Adres ayırma / yeniden ayırma / kaldırma
```javascript
wsjs.scope(async () => {
let me = wsjs.peer('me');
/**
* Sanal adresler size veri gönderilmek istendiğinde veya etkileşime
* geçilmesi istendiğinde ona socketId gibi bir UUID yerine sizi temsil eden daha kısa
* ip adresi, sayı veya kısa bir kod ile aynı şeyleri yapmanıza olanak tanır.
* Aynı anda hem sanal ip adres, sayı ve kısa koduna sahip olabilirsiniz
* ancak aynı türden temsil koduna (mesela kısa koddan) birden fazla sahip olamazsınız
* Yeni bir bağlantı daha açmanız gerekir
**/
// Bağlantınıze özel sanal tekil ip adresi kaynağı ayırın
let ipadress = await me.virtualPressure.allocAPIPAddress();
// Bağlantınıze özel sanal tekil numara kaynağı ayırın
let numberaddress = await me.virtualPressure.allocAPNumber();
// Bağlantınıze özel sanal kod kaynağı ayırın
let shortcodeadress = await me.virtualPressure.allocAPShortCode();
// Bütün bu kaynakları yenileriyle değiştirmek için
// her birinin ayrı ayrı yeniden alma işlevleri vardır
// Bir adresi yenilediğinizde artık eski adres kullanılmaz olur
me.virtualPressure.reallocAPIPAddress();
me.virtualPressure.reallocAPNumber();
me.virtualPressure.reallocAPShortCode();
// Bütün bu kaynakları kaldırmak için her birinin ayrı ayrı
// bırakma işlevi vardır
// Bir adresi kullanmadığınızda artık bu adreslerden size
// ulaşılamaz olursunuz
await me.virtualPressure.releaseAPIPAddress();
await me.virtualPressure.releaseAPNumber();
await me.virtualPressure.releaseAPShortCode();
await me.virtualPressure.queryAPIPAddress();
await me.virtualPressure.queryAPNumber();
await me.virtualPressure.queryAPShortCode();
})
```
Farklı bir eş ile iletişime geçme
```javascript
wsjs.scope(async () => {
let peer = wsjs.peer('325a8f7f-eaaf-4c21-855e-9e965c0d5ac9') // Diğer eşin socketId'sini belirtiyoruz
peer.on('message',(payload) => {
// Eş eğer mesaj gönderirse burası tetiklenecek ve gönderdiği mesaj payload değişkeni ile iletilir
})
// Mesaj göndermek için send fonksiyonunu kullanabilirsiniz
peer.send('Merhaba');
peer.send([29, true]);
peer.send({
type: 'notification',
value: "Hi!"
});
})
```

View File

@ -4,6 +4,8 @@ export interface IConnection{
timeout: number;
}
}
const RootURL : string = ( <HTMLScriptElement> document.currentScript).src
export class Connection
{
public ws! : WebSocket;
@ -15,7 +17,23 @@ export class Connection
public autoReconnectTimeout : number = 3000;
public autoReconnectTimer? : number;
constructor(options: IConnection){
this.endpoint = new URL(options.endpoint);
if(options.endpoint == "auto")
{
let scriptPath = new URL(RootURL);
let isSecurity = scriptPath.protocol == "https:";
let dumeUrl = scriptPath.pathname.split('/').slice(0,-1).join('/') + '/';
let wsSocket = new URL(dumeUrl, scriptPath);
wsSocket.protocol = isSecurity ? 'wss:' : 'ws:';
this.endpoint = new URL(wsSocket.href);
}else{
try{
// Testing
this.endpoint = new URL(options.endpoint);
}catch{
throw new Error("endpoint is required")
}
}
if(typeof options.autoReconnect == "boolean")
{
this.autoReconnect = true;
@ -36,6 +54,11 @@ export class Connection
}
public disconnect()
{
/**
* Eğer bilinerek elle kapatıldıysa otomatik tekrar bağlanmasının
* önüne geçmek için autoReconnect bayrağını her zaman kapalı tutmak gerekir
*/
this.autoReconnect = false;
this.ws.close();
}
public addWSEvents()
@ -54,6 +77,9 @@ export class Connection
}
private eventClose()
{
for (const callback of this.passiveConnectionEvent) {
callback(void 0);
}
this.connected = false;
if(this.autoReconnect)
{
@ -79,6 +105,16 @@ export class Connection
this.activeConnectionEvent.push(func);
}
}
private passiveConnectionEvent : Function[] = [];
public onPassive(func:Function)
{
if(!this.connected)
{
func()
}else{
this.passiveConnectionEvent.push(func);
}
}
private eventMessage(data: string | ArrayBuffer)
{
if(typeof data == "string")

View File

@ -17,7 +17,6 @@ export default class EventPool
{
return new Promise((ok,rej) => {
let id = ++this.count;
this.wsts.WSTSProtocol.SendRequest(msg, id);
this.events.set(id,[
(data:any) => {
ok(data);
@ -26,6 +25,7 @@ export default class EventPool
rej(data);
}
]);
this.wsts.WSTSProtocol.SendRequest(msg, id);
})
}
public stream(msg: Message, callback: Function)

View File

@ -29,11 +29,8 @@ export default class Peer extends EventTarget
super();
this.mwse = wsts;
this.info = new PeerInfo(this);
this.on('updateinfo',(name:string,value:any) => {
this.info.info[name] = value;
})
this.on('pack',(data:{type?:string,action?:IMessageSymbase,payload?:any}) => {
if(data.type == ':rtcbase_pack:')
if(data.type == ':rtcpack:')
{
if(this.rtc)
{
@ -56,7 +53,7 @@ export default class Peer extends EventTarget
})
this.rtc.on("output",(payload:object) => {
this.send({
type: ':rtcbase_pack:',
type: ':rtcpack:',
payload: payload
})
});
@ -121,6 +118,18 @@ export default class Peer extends EventTarget
value: 'disable'
});
}
async enablePairInfo(){
await this.mwse.EventPooling.request({
type: 'connection/pairinfo',
value: true
});
}
async disablePairInfo(){
await this.mwse.EventPooling.request({
type: 'connection/pairinfo',
value: false
});
}
async requestPair()
{
let {message,status} = await this.mwse.EventPooling.request({
@ -204,6 +213,9 @@ export default class Peer extends EventTarget
if(sendChannel == "websocket")
{
if(!this.mwse.writable){
return console.warn("Socket is not writable");
}
await this.mwse.EventPooling.request({
type:'pack/to',
pack,

View File

@ -22,6 +22,7 @@ export default class Room extends EventTarget
{
public mwse : MWSE;
public options! : IRoomOptions;
public config! : IRoomOptions;
public roomId? : string;
public accessType? : "public"|"private";
public description? : string;
@ -52,7 +53,7 @@ export default class Room extends EventTarget
autoFetchInfo: true
};
Object.assign(defaultOptions,options);
this.options = defaultOptions as IRoomOptions;
this.config = defaultOptions as IRoomOptions;
}
}
@ -60,21 +61,21 @@ export default class Room extends EventTarget
this.roomId = uuid;
}
async createRoom(roomOptions : IRoomOptions){
let options = this.options || roomOptions;
let config = this.config || roomOptions;
let result = await this.mwse.EventPooling.request({
type:'create-room',
...options
...config
});
if(result.status == 'fail')
{
if(result.message == "ALREADY-EXISTS" && this.options.ifexistsJoin)
if(result.message == "ALREADY-EXISTS" && this.config.ifexistsJoin)
{
return this.join();
}
throw new Error(result.message || result.messages);
}else{
this.options = {
...this.options,
...this.config,
...result.room
};
this.roomId = result.room.id;
@ -84,16 +85,16 @@ export default class Room extends EventTarget
async join(){
let result = await this.mwse.EventPooling.request({
type:'joinroom',
name: this.options.name,
credential: this.options.credential,
autoFetchInfo: this.options.autoFetchInfo || false
name: this.config.name,
credential: this.config.credential,
autoFetchInfo: this.config.autoFetchInfo || false
});
if(result.status == 'fail')
{
throw new Error(result.message);
}else{
this.options = {
...this.options,
...this.config,
...result.room
};
if(result.info)
@ -115,13 +116,33 @@ export default class Room extends EventTarget
this.mwse.rooms.delete(this.roomId as string);
}
}
async send(pack: any, wom:boolean = false){
await this.mwse.EventPooling.request({
type:'pack/room',
pack,
to: this.roomId,
wom
});
async send(pack: any, wom:boolean = false, handshake = false){
if(!this.mwse.writable){
return console.warn("Socket is not writable");
}
if(handshake)
{
let {type} = await this.mwse.EventPooling.request({
type:'pack/room',
pack,
to: this.roomId,
wom,
handshake
}) as {
type:"success"|"fail"
};
if(type == "fail"){
throw new Error("Cant send message to room")
}
}else{
await this.mwse.EventPooling.request({
type:'pack/room',
pack,
to: this.roomId,
wom,
handshake
})
}
}
async fetchPeers(filter?:{[key:string]:any}, onlyNumber:boolean = false) : Promise<Number | Peer[]>
{
@ -147,7 +168,7 @@ export default class Room extends EventTarget
throw new Error("Cant using peers on room")
}else if(status == 'success'){
for (const peerid of peers) {
let peer = this.mwse.peer(peerid);
let peer = this.mwse.peer(peerid,true);
cup.push(peer);
this.peers.set(peerid, peer);
}

View File

@ -6,6 +6,9 @@ export class RoomInfo
public info : {[key:string]: any} = {};
constructor(room : Room){
this.room = room;
this.room.on('updateinfo',(name:string,value:any) => {
this.info[name] = value;
})
};
public async fetch(name?:string)
{
@ -18,16 +21,16 @@ export class RoomInfo
}));
if(rinfo.status == "success")
{
this.info = rinfo.info;
this.info = rinfo.value;
}else console.warn(rinfo.message);
}else{
let rinfo = await this.room.mwse.EventPooling.request(({
type: "peer/info",
peer: this.room.roomId
type: "room/info",
roomId: this.room.roomId
}));
if(rinfo.status == "success")
{
this.info = rinfo.info;
this.info = rinfo.value;
}else console.warn(rinfo.message);
};
return this.info;

View File

@ -142,7 +142,7 @@ export default class WebRTC
case "streamRemoved":{
let {id} = data;
this.emit('stream:stopped', this.recaivingStream.get(id));
this.sendingStream.delete(id);
this.recaivingStream.delete(id);
break;
}
case "streamAccept":{
@ -448,7 +448,7 @@ setInterval(()=>{
window.URL.revokeObjectURL(this.src);
};
WebRTC.requireGC = false;
}, 3000)
}, 3000);
declare global {
interface MediaStream {

View File

@ -17,6 +17,21 @@ export default class MWSE extends EventTarget {
public peers : Map<string, Peer> = new Map();
public virtualPressure : IPPressure;
public me! : Peer;
/*public static compress(message:string, callback:(e:any) => any)
{
let u : any= [];
let C = new Gzip({
level: 9,
mem: 12
},(stream,isLast) => {
u.push(stream);
if(isLast)
{
callback(u);
}
});
C.push(new TextEncoder().encode(message), true);
}*/
constructor(options: IConnection){
super();
this.server = new Connection(options);
@ -35,8 +50,45 @@ export default class MWSE extends EventTarget {
this.emit('scope');
this.activeScope = true;
});
this.server.onPassive(async ()=>{
this.emit('close');
});
this.packMessagingSystem();
}
public writable = 1;
public readable = 1;
public destroy()
{
this.server.disconnect();
}
public enableRecaiveData(){
this.WSTSProtocol.SendOnly({ type: 'connection/packrecaive', value: 1 })
this.readable = 1
}
public disableRecaiveData(){
this.WSTSProtocol.SendOnly({ type: 'connection/packrecaive', value: 0 })
this.readable = 0
}
public enableSendData(){
this.WSTSProtocol.SendOnly({ type: 'connection/packsending', value: 1 })
this.writable = 1
}
public disableSendData(){
this.WSTSProtocol.SendOnly({ type: 'connection/packsending', value: 0 })
this.writable = 0
}
public enableNotifyRoomInfo(){
this.WSTSProtocol.SendOnly({ type: 'connection/roominfo', value: 1 })
}
public disableNotifyRoomInfo(){
this.WSTSProtocol.SendOnly({ type: 'connection/roominfo', value: 0 })
}
public async request(peerId: string, pack:Message)
{
let {pack:answer} = await this.EventPooling.request({
@ -58,8 +110,11 @@ export default class MWSE extends EventTarget {
private packMessagingSystem()
{
this.EventPooling.signal('pack',(payload : {from:string,pack:any}) => {
let {from,pack} = payload;
this.peer(from, true).emit('pack', pack);
if(this.readable)
{
let {from,pack} = payload;
this.peer(from, true).emit('pack', pack);
}
})
this.EventPooling.signal('request',(payload : {from:string,pack:any,id:number}) => {
let {from,pack, id} = payload;
@ -74,8 +129,11 @@ export default class MWSE extends EventTarget {
this.peer('me').emit('request', scope);
})
this.EventPooling.signal('pack/room',(payload : {from:string,pack:any,sender:string}) => {
let {from,pack,sender} = payload;
this.room(from).emit('message', pack, this.peer(sender));
if(this.readable)
{
let {from,pack,sender} = payload;
this.room(from).emit('message', pack, this.peer(sender));
}
})
this.EventPooling.signal('room/joined',(payload : {id:string,roomid:any,ownerid:string}) => {
let {id,roomid} = payload;
@ -91,7 +149,7 @@ export default class MWSE extends EventTarget {
this.EventPooling.signal('room/ejected',(payload : {id:string,roomid:any,ownerid:string}) => {
let {id,roomid} = payload;
let room = this.room(roomid);
let peer = this.peer(id);
let peer = this.peer(id, true);
room.peers.delete(peer.socketId as string);
room.emit('eject', peer);
})
@ -104,27 +162,32 @@ export default class MWSE extends EventTarget {
})
this.EventPooling.signal("pair/info", (payload : {from : string,name: string, value: string | number | boolean}) => {
let {from, name, value} = payload;
let peer = this.peer(from);
let peer = this.peer(from, true);
peer.info.info[name] = value;
peer.emit("info", name, value);
})
this.EventPooling.signal("request/pair", (payload : {from : string,info: any}) => {
let {from, info} = payload;
let peer = this.peer(from);
let peer = this.peer(from, true);
peer.info.info = info;
peer.emit("request/pair", peer);
this.peer('me').emit('request/pair', peer);
})
this.EventPooling.signal("peer/disconnect", (payload : {id : string}) => {
let {id} = payload;
let peer = this.peer(id, true);
peer.emit("disconnect", peer);
})
this.EventPooling.signal("accepted/pair", (payload : {from : string,info: any}) => {
let {from, info} = payload;
let peer = this.peer(from);
let peer = this.peer(from, true);
peer.info.info = info;
peer.emit("accepted/pair", peer);
this.peer('me').emit('accepted/pairr', peer);
})
this.EventPooling.signal("end/pair", (payload : {from : string,info: any}) => {
let {from, info} = payload;
let peer = this.peer(from);
let peer = this.peer(from, true);
peer.emit("endPair", info);
this.peer('me').emit('endPair', from, info);
})

View File

@ -2,7 +2,6 @@
const cluster = require("cluster");
const os = require("os");
let {randomUUID} = require("crypto");
const { LOADBALANCE_CPU } = require('./Source/config');
/**
* Use Round Robin algorithm for cluster process load balancer
@ -11,16 +10,6 @@ const { LOADBALANCE_CPU } = require('./Source/config');
async function main()
{
if(!LOADBALANCE_CPU)
{
console.log("Slave Process PID:", process.pid);
// Emit from slave
process.send = new Function();
// Compile source code and run
require("./Source/index");
// stay here
return;
}
if(cluster.isPrimary == false)
{
console.log("Slave Process PID:", process.pid);
@ -37,7 +26,7 @@ async function main()
// Worker process list
const master = new Map();
const coreCount = LOADBALANCE_CPU//os.cpus().length;
const coreCount = 1 //os.cpus().length;
for(let index = 0; index < coreCount; index++)
{
// Open slave process

9
package-lock.json generated
View File

@ -15,7 +15,6 @@
"fflate": "^0.8.1",
"joi": "^17.11.0",
"knex": "^3.0.1",
"moment": "^2.29.4",
"sqlite3": "^5.1.6",
"systemjs": "^6.14.2",
"terminal": "^0.1.4",
@ -2620,14 +2619,6 @@
"node": ">=10"
}
},
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",

View File

@ -45,7 +45,6 @@
"fflate": "^0.8.1",
"joi": "^17.11.0",
"knex": "^3.0.1",
"moment": "^2.29.4",
"sqlite3": "^5.1.6",
"systemjs": "^6.14.2",
"terminal": "^0.1.4",

View File

@ -4,6 +4,10 @@ html,body{
}
body{
background-color: #141414;
overflow: hidden;
}
*{
box-sizing: border-box;
}
.root{
gap: 10px;
@ -13,67 +17,36 @@ body{
flex-wrap: nowrap;
flex-direction: row;
box-sizing: border-box;
}
.dialoque{
flex: 1;
display: flex;
flex-wrap: nowrap;
flex-direction: row;
position: relative;
overflow: hidden;
}
.videolist{
flex: 0 0 200px;
display: flex;
flex-wrap: nowrap;
flex-direction: column;
width: 100%;
height: 100%;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(40vh, 1fr));
grid-template-rows: repeat(auto-fit, minmax(23vh, 1fr));
gap: 10px;
overflow: auto;
padding-right: 10px;
cursor: pointer;
}
.dialoque #primaryVideo{
width: 100%;
height: 100%;
box-sizing: border-box;
object-fit: contain;
background-color: transparent/*#282828*/;
border-radius: 10px;
z-index: 2;
}
.dialoque #primaryVideoShadow{
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 10px;
position: absolute;
filter: brightness(0.5);
z-index: 1;
left: 0;
top: 0;
}
.dialoque #secondaryVideo{
width: 300px;
height: 200px;
min-width: 300px;
max-width: 30%;
min-height: 200px;
max-height: 35%;
position: absolute;
right: 10px;
bottom: 10px;
background-color: #383838;
border-radius: 10px;
z-index: 3;
outline-offset: 0px;
outline-style: solid;
outline-width: 5px;
outline-color: rgba(0,0,0,.5);
justify-content: center;
align-items: center;
overflow: hidden;
}
.videolist .frame{
max-width: 200px;
border: solid 2px white;
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
align-items: center;
border-radius: 20px;
box-sizing: border-box;
padding: 1px;
overflow: hidden;
position: relative;
}
.videolist .frame > video{
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 10px;
border: solid 3px transparent;
}
.videolist .frame.active{
border: solid 3px green;
@ -92,16 +65,36 @@ video{
::-webkit-scrollbar-track {
background-color: #ffffff1f;
}
#stats{
.tool-container{
position: absolute;
z-index: 3;
font-size: 14px;
line-height: 1.5em;
color: white;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
padding: 10px;
text-shadow: 0 0 5px black;
background-color: rgba(0,0,0,.4);
overflow: auto;
max-height: 80vh;
}
left: 0;
right: 0;
top: 0;
bottom: 0;
display: flex;
}
.tool-container > .tools{
margin: auto;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 10px;
overflow: hidden;
}
.tool-container > .tools > button{
border: none;
color: white;
vertical-align: middle;
cursor: pointer;
padding: 0 10px;
}
.tool-container > .tools > button:not(:hover){
background-color: transparent;
}
.tool-container > .tools > button:hover{
background-color: rgba(255,255,255,0.2);
}
.tool-container > .tools > button i{
vertical-align: middle;
line-height: 2;
}

View File

@ -6,20 +6,15 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>saQüt Video Streaming</title>
<script src="https://ws.saqut.com/script"></script>
<link rel="stylesheet" href="./index.css">
<link rel="stylesheet" href="./index.css?v=42">
</head>
<body>
<div class="root">
<div class="dialoque">
<video id="primaryVideo" muted playsinline autoplay></video>
<video id="primaryVideoShadow" muted playsinline autoplay></video>
<div id="stats"></div>
</div>
<div class="videolist">
</div>
</div>
<script src="index.js?v=32"></script>
<script src="index.js?v=42"></script>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</body>
</html>

View File

@ -22,39 +22,19 @@ let roomid;
* @type {import("./MWSE/Room").default}
*/
let room;
/**
* @type {MediaStream}
*/
let primaryVideoContent;
/**
* @type {HTMLVideoElement}
*/
let primaryVideo;
/**
* @type {HTMLVideoElement}
*/
let primaryVideoShadow;
/**
* @type {HTMLVideoElement}
*/
let secondaryVideo;
/**
* @type {MediaStream}
*/
let outgoingStream;
/**
* @type {MediaStream}
*/
let outgoingStreamOnlyVideo;
/**
* @type {HTMLDivElement}
*/
let videoContainer = document.querySelector(".videolist");
let activePeerInfos = {};
let activePeer = null;
let maxbitrate;
let resulation;
let activePeers = {}
let ofscreencanvas = document.createElement("canvas");
function connect()
{
@ -64,58 +44,64 @@ function connect()
mwse.scope(beginEngine);
}
let interact = false;
setInterval(()=>{
document.querySelectorAll(".soundon").forEach(e => (e.muted = 0,e.play()));
},1000)
document.addEventListener("click",()=>{
interact = true;
document.querySelectorAll(".soundon").forEach(e => (e.muted = 0,e.play()));
})
/**
* @type {HTMLVideoElement}
*/
let activeVideo;
function setPrimaryVideo(video, soundOn)
function templateVideo(name, stream,infinitedMute)
{
primaryVideo.srcObject = video;
primaryVideoShadow.srcObject = video;
if(soundOn == undefined)
let t = new DOMParser().parseFromString(`
<div class="frame">
<video autoplay playsinline muted data-name="${name}">
</video>
<div class="tool-container">
<div class="tools">
<button>
<i class="material-icons">home</i>
</button>
<button>
<i class="material-icons">close</i>
</button>
</div>
</div>
</div>
`,"text/html");
let i = t.querySelector("video");
if(infinitedMute == true)
{
primaryVideo.muted = 1;
primaryVideo.volume = 0;
}else if(soundOn){
primaryVideo.muted = 0;
primaryVideo.volume = 1;
}else{
primaryVideo.muted = 0;
primaryVideo.volume = 0;
i.muted = 1;
}else if(interact == false)
{
i.muted = 1;
i.classList.add("soundon");
}
}
function setSecondaryVideo(video)
{
// secondaryVideo.srcObject = video;
}
function templateVideo(name, stream)
{
let i = document.createElement("video");
i.muted = 1;
i.classList.add("frame")
i.playsInline = 1;
i.autoplay = 1;
i.dataset.name = name;
if(stream) i.srcObject = stream;
return i;
return t.querySelector("div");
}
function addVideoList(name, stream, peer)
function addVideoList(name, stream, peer, infinitedMute)
{
if(!videoContainer.querySelector(`[name="${name}"]`))
{
let video = templateVideo(name, stream);
let video = templateVideo(name, stream, infinitedMute);
video.dataset.user = peer.socketId;
video.onclick = function(){
if(activeVideo)
{
activeVideo.classList.remove("active");
};
video.classList.add("active");
activeVideo = video;
activePeer = peer.rtc.rtc;
setPrimaryVideo(stream, true);
};
videoContainer.appendChild(video);
}
}
@ -128,7 +114,6 @@ function removeVideoList(name)
if(k.dataset.user == activeVideo?.dataset.user || !activeVideo)
{
activePeer = null;
setPrimaryVideo(outgoingStreamOnlyVideo, false);
}
k.remove();
}
@ -145,24 +130,6 @@ async function beginEngine()
let url = new URL(window.location);
roomid = url.searchParams.get("room");
if(url.searchParams.get("maxbitrate"))
{
let n = Number(url.searchParams.get("maxbitrate"));
if(Number.isFinite(n) && !Number.isNaN(n))
{
maxbitrate = n;
}else maxbitrate = 3_000_000;
}else maxbitrate = 3_000_000;
if(url.searchParams.get("resulation"))
{
let n = Number(url.searchParams.get("resulation"));
if(Number.isFinite(n) && !Number.isNaN(n))
{
resulation = n;
}else resulation = 1.5;
}else resulation = 1.5;
if(!!roomid == 0)
{
let hash = window.crypto.randomUUID();
@ -171,18 +138,34 @@ async function beginEngine()
};
connectRoom(roomid);
if(url.searchParams.get("maxbitrate"))
{
let n = Number(url.searchParams.get("maxbitrate"));
if(Number.isFinite(n) && !Number.isNaN(n))
{
maxbitrate = n;
}else maxbitrate = 2500_000;
}else maxbitrate = 2500_000;
if(url.searchParams.get("resulation"))
{
let n = Number(url.searchParams.get("resulation"));
if(Number.isFinite(n) && !Number.isNaN(n))
{
resulation = n;
}else resulation = 1.2;
}else resulation = 1.2;
};
window.addEventListener("load", () => {
primaryVideo = document.querySelector("#primaryVideo");
secondaryVideo = document.querySelector("#secondaryVideo");
primaryVideoShadow = document.querySelector("#primaryVideoShadow");
connect()
});
async function startOutgoingWebcam()
{
outgoingStream = await navigator.mediaDevices.getUserMedia({
let mediaStream = await navigator.mediaDevices.getUserMedia({
video: /*true*/{
advanced: [
{ width: { exact: 1920 } },
@ -197,13 +180,13 @@ async function startOutgoingWebcam()
{ width: { exact: 240 } }
],
facingMode: "user"
},/**/
audio: false
}
});
outgoingStreamOnlyVideo = new MediaStream(outgoingStream);
// outgoingStreamOnlyVideo.removeTrack(outgoingStreamOnlyVideo.getAudioTracks()[0])
outgoingStream = mediaStream;
}
async function connectRoom()
{
await startOutgoingWebcam();
@ -226,13 +209,8 @@ async function connectRoom()
for (const peer of await room.fetchPeers()) {
IncomingPeer(peer)
}
if(!primaryVideoContent)
{
setPrimaryVideo(outgoingStreamOnlyVideo);
}
setSecondaryVideo(outgoingStreamOnlyVideo);
addVideoList("My Webcam",outgoingStreamOnlyVideo, mwse.peer("me"))
addVideoList("My Webcam",outgoingStream, mwse.peer("me"), true)
};
@ -241,13 +219,12 @@ async function connectRoom()
*/
function IncomingPeer(peer,activeConnect)
{
let sendedOTP = false;
peer.createRTC({
iceCandidatePoolSize: 0
},[{
urls: "turn:161.97.136.175:3478",
username: "argist-eu-east-25",
credential: "ee7df17eed35f4cf5a207777f3c0cd7d3b1901a5de7aff52ea55742289d7fee2"
urls: "turn:20.166.82.187:3478",
username: "turnserver",
credential: "turnserver"
},{
urls: "stun:stun.l.google.com:19302"
}]);
@ -257,29 +234,21 @@ function IncomingPeer(peer,activeConnect)
}
peer.rtc.rtc.turboBitrate = 0;
peer.rtc.on('connected',() => {
if(!activeConnect && !sendedOTP)
if(!activeConnect)
{
sendedOTP = true;
peer.rtc.sendStream(outgoingStream, "Webcam", {});
activePeers[peer.socketId] = peer.rtc.rtc;
}
});
peer.rtc.on('disconnected',() => {
removeVideoList(peer.streamY, peer);
delete activePeers[peer.socketId];
});
peer.rtc.on("stream:added", ({stream,name}) => {
peer.streamY = peer.socketId + " | " + name + " - " + stream.id;
addVideoList(peer.socketId + " | " + name + " - " + stream.id,stream, peer);
if(!primaryVideoContent)
if(activeConnect)
{
activePeer = peer.rtc.rtc;
primaryVideoContent = stream;
setPrimaryVideo(primaryVideoContent, true);
}
if(activeConnect && !sendedOTP)
{
sendedOTP = true;
peer.rtc.sendStream(outgoingStream, "Webcam", {});
}
})
@ -292,234 +261,25 @@ function OutgoingPeer(peer)
removeVideoList(peer.streamY, peer);
}
setInterval(()=>{
getStats()
}, 1000)
let relative;
setInterval(() => {
if(activePeer?.turboBitrate === 0)
for(const [,peerRtc] of Object.entries(activePeers))
{
const senders = activePeer.getSenders();
const videoSender = senders.find(sender => sender.track?.kind === 'video');
if(videoSender)
if(peerRtc?.turboBitrate !== 1)
{
const parameters = videoSender.getParameters();
parameters.encodings[0].maxBitrate = maxbitrate;
parameters.encodings[0].scaleResolutionDownBy = resulation;
videoSender.setParameters(parameters).then(() => {
// console.log('Bitrate değiştirildi.');
activePeer.turboBitrate = 1;
})
.catch(error => {
// console.error('Bitrate değiştirilirken bir hata oluştu:', error);
activePeer.turboBitrate = -1;
});
const senders = peerRtc.getSenders();
const videoSender = senders.find(sender => sender.track?.kind === 'video');
if(videoSender){
const parameters = videoSender.getParameters();
parameters.encodings[0].maxBitrate = maxbitrate;
parameters.encodings[0].scaleResolutionDownBy = resulation;
videoSender.setParameters(parameters).then(() => {
peerRtc.turboBitrate = 1;
});
}
}
}
}, 5000);
let relative;
async function getStats()
{
let stats = {};
if(!activePeer) return document.querySelector("#stats").innerHTML = "";
let stat = await activePeer.getStats();
let certs = {};
let selectedCandidatePairId;
let remotes = {};
let locals = {};
let candidatePairs = {};
for (const [id, data] of stat) {
switch(data.type)
{
case "candidate-pair":{
if(data.state == "succeeded")
{
let t = {};
t.candidateBytesSent = data.bytesSent;
t.candidateBytesReceived = data.bytesReceived;
t.candidatePacketsReceived = data.packetsReceived;
t.candidatePacketsSent = data.packetsSent;
t.candidateTotalRoundTripTime = data.totalRoundTripTime;
t.currentRoundTripTime = data.currentRoundTripTime;
t.candidateRemoteCandidateId = data.remoteCandidateId;
t.candidateLocalCandidateId = data.localCandidateId;
candidatePairs[data.id] = t;
}
break;
}
case "remote-candidate":{
let t = {};
t.remoteCandidateType = data.candidateType;
t.remoteProtocol = data.protocol;
t.remoteAddress = data.address;
t.remoteIsRemote = data.isRemote;
t.remotePort = data.port;
remotes[data.id] = t;
break;
}
case "local-candidate":{
let t = {};
t.localCandidateType = data.candidateType;
t.localProtocol = data.protocol;
t.localAddress = data.address;
t.localIsRemote = data.isRemote;
t.localPort = data.port;
locals[data.id] = t;
break;
}
case "inbound-rtp":{
let k = stats["inbound" + data.mediaType] = {};
k.mediaType = data.mediaType;
k.trackIdentifier = data.trackIdentifier;
k.bytesReceived = data.bytesReceived;
k.totalSamplesReceived = data.totalSamplesReceived;
k.totalSamplesDuration = data.totalSamplesDuration;
k.audioLevel = data.audioLevel;
k.packetsReceived = data.packetsReceived;
k.framesReceived = data.framesReceived;
k.frameWidth = data.frameWidth;
k.frameHeight = data.frameHeight;
k.framesPerSecond = data.framesPerSecond;
k.totalDecodeTime = data.totalDecodeTime;
k.totalProcessingDelay = data.totalProcessingDelay;
k.totalAssemblyTime = data.totalAssemblyTime;
k.decoderImplementation = data.decoderImplementation;
k.powerEfficientDecoder = data.powerEfficientDecoder;
break;
}
case "outbound-rtp":{
let k = stats["outbound" + data.mediaType] = {};
k.mediaType = data.mediaType;
k.bytesSent = data.bytesSent;
k.framesEncoded = data.framesEncoded;
k.totalEncodeTime = data.totalEncodeTime;
k.frameWidth = data.frameWidth;
k.frameHeight = data.frameHeight;
k.framesPerSecond = data.framesPerSecond;
k.framesSent = data.framesSent;
k.encoderImplementation = data.encoderImplementation;
k.powerEfficientEncoder = data.powerEfficientEncoder;
k.qualityLimitationReason = data.qualityLimitationReason;
break;
}
case "transport":{
stats.srtpCipher = data.srtpCipher;
stats.dtlsCipher = data.dtlsCipher;
stats.localcert = certs[data.localCertificateId];
stats.remotecert = certs[data.localCertificateId];
selectedCandidatePairId = data.selectedCandidatePairId;
break;
}
case "certificate":{
certs[data.id] = {
fingerprintAlgorithm: data.fingerprintAlgorithm,
fingerprint: data.fingerprint
};
break;
}
}
};
if(selectedCandidatePairId)
{
let pair = candidatePairs[selectedCandidatePairId];
if(!pair.candidateRemoteCandidateId)
{
return;
}
stats = {
...stats,
...pair,
...remotes[pair.candidateRemoteCandidateId],
...locals[pair.candidateLocalCandidateId]
};
}else{
let pair = Object.values(candidatePairs)[0];
stats = {
...stats,
...pair,
...Object.values(remotes)[0],
...Object.values(locals)[0]
};
}
if(!stats.inboundvideo)
{
return document.querySelector("#stats").innerHTML = "";
}
if(!relative)
{
relative = stats;
}
let turnused = stats.localCandidateType == "relay" || stats.remoteCandidateType == "relay";
document.querySelector("#stats").innerHTML = `
Signalization Server: ws.saqut.com<br>
TURN Server: Adjusted / ${turnused ? "Using now":"Never used"}<br>
STUN Server: Adjusted / used<br>
Websocket: Active connected<hr>
Local Candidate Type: ${{
host:"Direct Connection",
srflx: "Behind NAT (with STUN)",
prflx: "Proxy Connection (with STUN)",
relay: "TURN Server Connection"
}[stats.localCandidateType]}<br>
Local Protocol: ${stats.localProtocol}<br>
Local Address: ${stats.localAddress}<br>
Local Port: ${stats.localPort}<hr>
Remote Candidate Type: ${{
host:"Direct Connection",
srflx: "Behind NAT (with STUN)",
prflx: "Proxy Connection (with STUN)",
relay: "TURN Server Connection"
}[stats.remoteCandidateType]}<br>
Remote Protocol: ${stats.remoteProtocol}<br>
Remote Address: ${stats.remoteAddress}<br>
Remote Port: ${stats.remotePort}<hr>
Connection Total Bytes Send: Total: ${hfs(stats.candidateBytesSent)} | Current : ${hfs(stats.candidateBytesSent - relative.candidateBytesSent)}<br>
Connection Total Bytes Recaived: Total: ${hfs(stats.candidateBytesReceived)} | Current : ${hfs(stats.candidateBytesReceived - relative.candidateBytesReceived)}<br>
Connection Round Trip Time: ${stats.currentRoundTripTime}ms<hr>
Incoming Video Decoder: ${stats.inboundvideo.decoderImplementation}<br>
Incoming Frame Width: ${stats.inboundvideo.frameWidth}px<br>
Incoming Frame Height: ${stats.inboundvideo.frameHeight}px<br>
Incoming Frame Per Second: ${stats.inboundvideo.framesPerSecond}<br>
Incoming Decode Time (CPU Time): ${(stats.inboundvideo.totalDecodeTime - relative.inboundvideo.totalDecodeTime).toFixed(3)}ms<hr>
Outgoing Video Encoder: ${stats.outboundvideo.encoderImplementation}<br>
Outgoing Frame Width: ${stats.outboundvideo.frameWidth}px<br>
Outgoing Frame Height: ${stats.outboundvideo.frameHeight}px<br>
Outgoing Frame Per Second: ${stats.outboundvideo.framesPerSecond}<br>
Outgoing Decode Time (CPU Time): ${(stats.outboundvideo.totalEncodeTime - relative.outboundvideo.totalEncodeTime).toFixed(3)}ms<br>
Quality Increase Limitator: ${stats.outboundvideo.qualityLimitationReason}<hr>
Crypting SRTP Cipher: ${stats.srtpCipher}<br>
Crypting D-TLS Cipher: ${stats.dtlsCipher}<br>
${stats.localcert ? `EndToEnd Crypto Local Cert. Algoritm: ${stats.localcert.fingerprintAlgorithm}<br>
EndToEnd Crypto Remote Cert. Algoritm: ${stats.remotecert.fingerprintAlgorithm}<hr>` : ''}
Bitrate Booster: ${activePeer.turboBitrate==0?"Waiting conditions":activePeer.turboBitrate==1?"Boosting":"Fail"}<br>
Codec Handcooling : Failed<br>
Hardware Accelerated : Failed<hr>
MWSE Version: 0.7.0-beta<br>
Load Balancing: Active<br>
Max P2P Hardware Connection: ~${((Math.random()*5)|0) + 72}<br>
Max P2P Software Connection: ~24<br>
Max Server Connection: 231<br>
SDP Adaptation: Long time session<br>
`;
relative = stats;
};
function hfs(size) {
if(!size)return 0;
var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
}
},1000);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -22,7 +22,7 @@
<service md5="a35aeef398a27556cf9e08aa6e25650c" checkout="true" status="verified">
8305f8dd0b412a4b95ef71a44a2cb069890895668054436640b9dffd2b0db4d84e21788b2cf8d609eb95773b4ef3c926335bc93bdc4df971a95f4c4c68ee627402c8935aa619ad276a6994004babf0bb41a8ca9fd5d22e76ec68f662eeb449ad9cdbd37f78b52fb88101e86a54323be7cd4777fdab26ada1cbba1ff0b08b6f451aa45daadcf9ef35a0a24039f2b1d5ca88ad64057afdbfe7c2bba6033380208d8d7c6c5f822a5f8bf19b5ee4f16efe068bd914d2e6ca831af24d53fa330c5d8150eb52fdd29518c14ecbc9af7e68a6e5f7a866150ecccf2b598fd6ba78521d5fe815797a6430ea09d6b394abe630df9510046bc983d9c41e3213868aeaa9dd531220959dd6305a1c582bde1a3e01d9b4be5d09150c1f4a84aa35c995103e6c6e3b6bb5345759b2219a31d05626373653d8151130ba1abef2aa78494f736792c0a462f54ebd86d2a110ae27ad1ad413baca5c7c4a302252da1423e44647cd57399e92cc805a60ce43bbda7a74797be1ad0275c0b270be2f117bd9ccc07bee42e247d178b15ba233535bffbb1c641cdd9ca3d387985b7980e8aed9752dbe30313f8a4b911f0ac7d8fd9352e3be80cdfaa3fd20ac5771c9f918d456b88cd3155de13a3f71bbcc627e766a16a86cac17f83fde98a2e7742bee4144ad235e23c76459522a96812819585155b6fcb6a08dafb19ccf93de37cb6738c803f73b0876e583d47ea3c59d6aba7b168c8de113a8f04cb813847a79682574ee71f6d9a5987b5e3be7169bf3bd82fbda4f5899e31b55b8c257f989516bbc879e60a49a4c5370230b4f728dd9389a059b481ea61046230e1382046955dae636005db580ec4a0540ee382fbc74b78b00ec821a19778fc119b673496df2bc2f11639922141d4cbb8fe22833f6fb210801419ab1b43f77a2baf892055a41c57e529026d4784aad469a4d2b530e81780f2aaea6b37b44243fa0f5e60a99391c6cf2e8a0f263a38566bfa56f761684b754bb8fa95aed80511332bff4673d600b6a09852cba34faca550f0652241a9eb04ca9652c4a19c20d37d6772604c3bbd34c82fa913a1a95e33b811f33952f4618d3600165574e29c44f7f748c05f90105791906e5920b356c4a8ce133dc2a809f0e47aa0ca2537a09688d2b92d49fa6b1f25165b6d2905ea99d1d6d56b2c39b08167df0cf18ce0f593228abbdbba718f05de4700ffa54067cb01fa65d9e378c0bc88d5f4d7bc153db964920e3c0dab30c02e87d957c369c6492bba7b240c27541d80bf0d39cbe62c077bdf6b1fb252d470cc39f9e43a13a95c08788dd5fe4e62fa3b07be01496bc977c6e1fe62977a2a8b72197fb3ad812369e96e3dd2ad329f134aaa4baee81c2ebd1b2c39e55fd9afcc554633ffb04a827ac75f7b26e49bcc341ea062ba13f672cd0a1dfd79a093273a5187e3adb6ece0e460555dc872c49b95e5054ffb772a4301feecdb072d6087345814b4af54de55d413eee562be25741a041bbdcb2850281
</service>
<service md5="af214a23bf109f45d423f0fdcad927f4" checkout="true" status="fail">
<service md5="af214a23bf109f45d423f0fdcad927f4" checkout="true" status="verified">
fb7d736ac8ee7a3a74f952551e8856a34c05d564ee0408ca0eea47a86e833b5b30a732b9d3ca954e56231908beb9022aec02c756c3e075bd9086f9ade0381b953185b4d3ed332d5c4b454abe5d2ccbeb48b473f956d543bde4f48c1978f129da7bf481b629a8da09941e5ea66d069617f6c82ac3a685b9e6cf75d8d4faf0b2ca0b52df201372c6e2d08dbcb2b724da94c733590165282e6faefd7bb89bb5be76d452f84b50a557d4b0f45192fc562f3682aba1098818ed3ddfaf72181d66f1f7b59be2b31759b66c
</service>
</unit>
@ -48,7 +48,7 @@
<service md5="a35aeef398a27556cf9e08aa6e25650c" checkout="true" status="verified">
8305f8dd0b412a4b95ef71a44a2cb069890895668054436640b9dffd2b0db4d84e21788b2cf8d609eb95773b4ef3c926335bc93bdc4df971a95f4c4c68ee627402c8935aa619ad276a6994004babf0bb41a8ca9fd5d22e76ec68f662eeb449ad9cdbd37f78b52fb88101e86a54323be7cd4777fdab26ada1cbba1ff0b08b6f451aa45daadcf9ef35a0a24039f2b1d5ca88ad64057afdbfe7c2bba6033380208d8d7c6c5f822a5f8bf19b5ee4f16efe068bd914d2e6ca831af24d53fa330c5d8150eb52fdd29518c14ecbc9af7e68a6e5f7a866150ecccf2b598fd6ba78521d5fe815797a6430ea09d6b394abe630df9510046bc983d9c41e3213868aeaa9dd531220959dd6305a1c582bde1a3e01d9b4be5d09150c1f4a84aa35c995103e6c6e3b6bb5345759b2219a31d05626373653d8151130ba1abef2aa78494f736792c0a462f54ebd86d2a110ae27ad1ad413baca5c7c4a302252da1423e44647cd57399e92cc805a60ce43bbda7a74797be1ad0275c0b270be2f117bd9ccc07bee42e247d178b15ba233535bffbb1c641cdd9ca3d387985b7980e8aed9752dbe30313f8a4b911f0ac7d8fd9352e3be80cdfaa3fd20ac5771c9f918d456b88cd3155de13a3f71bbcc627e766a16a86cac17f83fde98a2e7742bee4144ad235e23c76459522a96812819585155b6fcb6a08dafb19ccf93de37cb6738c803f73b0876e583d47ea3c59d6aba7b168c8de113a8f04cb813847a79682574ee71f6d9a5987b5e3be7169bf3bd82fbda4f5899e31b55b8c257f989516bbc879e60a49a4c5370230b4f728dd9389a059b481ea61046230e1382046955dae636005db580ec4a0540ee382fbc74b78b00ec821a19778fc119b673496df2bc2f11639922141d4cbb8fe22833f6fb210801419ab1b43f77a2baf892055a41c57e529026d4784aad469a4d2b530e81780f2aaea6b37b44243fa0f5e60a99391c6cf2e8a0f263a38566bfa56f761684b754bb8fa95aed80511332bff4673d600b6a09852cba34faca550f0652241a9eb04ca9652c4a19c20d37d6772604c3bbd34c82fa913a1a95e33b811f33952f4618d3600165574e29c44f7f748c05f90105791906e5920b356c4a8ce133dc2a809f0e47aa0ca2537a09688d2b92d49fa6b1f25165b6d2905ea99d1d6d56b2c39b08167df0cf18ce0f593228abbdbba718f05de4700ffa54067cb01fa65d9e378c0bc88d5f4d7bc153db964920e3c0dab30c02e87d957c369c6492bba7b240c27541d80bf0d39cbe62c077bdf6b1fb252d470cc39f9e43a13a95c08788dd5fe4e62fa3b07be01496bc977c6e1fe62977a2a8b72197fb3ad812369e96e3dd2ad329f134aaa4baee81c2ebd1b2c39e55fd9afcc554633ffb04a827ac75f7b26e49bcc341ea062ba13f672cd0a1dfd79a093273a5187e3adb6ece0e460555dc872c49b95e5054ffb772a4301feecdb072d6087345814b4af54de55d413eee562be25741a041bbdcb2850281
</service>
<service md5="af214a23bf109f45d423f0fdcad927f4" checkout="true" status="fail">
<service md5="af214a23bf109f45d423f0fdcad927f4" checkout="true" status="verified">
fb7d736ac8ee7a3a74f952551e8856a34c05d564ee0408ca0eea47a86e833b5b30a732b9d3ca954e56231908beb9022aec02c756c3e075bd9086f9ade0381b953185b4d3ed332d5c4b454abe5d2ccbeb48b473f956d543bde4f48c1978f129da7bf481b629a8da09941e5ea66d069617f6c82ac3a685b9e6cf75d8d4faf0b2ca0b52df201372c6e2d08dbcb2b724da94c733590165282e6faefd7bb89bb5be76d452f84b50a557d4b0f45192fc562f3682aba1098818ed3ddfaf72181d66f1f7b59be2b31759b66c
</service>
</unit>
@ -62,19 +62,19 @@
<service md5="2836d60b7e3a9a41f75d662d63c65197" checkout="true" status="verified">
35b9ea4d98445d6fda595e42c921269e0699ed78a2655b169fcaffef66521e649c1818cb71fb5a2ef4c0f453786de08c9cf81cdc4f6bcb8a7af68f23d9a4b3dc4a84950a13261e929ef4aa41a53819b8fc98582250533b476e46d6d0894a0003f315a6d8612d2d46eebe367330ae4587635daa4d4b929a33de91dfdb30b1aa3b3bded0ab06741786b19564b74e3b0d86a3d056bf9662fa004d2609a62b862caea7f6448a5257689a0de69798ac8eaf6186799c1e5de7ba124493365a76ded957d0f133190608b49c940cd7db6bdcb6b98739dfe6dc44b4736ebb10a3563ac7a4c6c63177cb292f32be6be3041cd2c27fef4c9f3ec2005041f80df142941185c57114d90500c744d9442f4507e43c91bbacc4205ee8dc4dbbc0656a6bf948309c446ffc67844f8e49171672ebbf380368979e20dcd114f889936f5a6ae0fdc393d4292659ee8f00070c0b6f01781e9c9d64a1ef7dc43912de5666d2f3495d9e4f3b478621e0e7e2ade6728e2de162aaf06b000ef5c514ba8004ed28cdbb9893a2b9e024a3ffedf98b920ff94130f31775cbc6a4f7a0dd361c40a8ca0956bf7fa4047bb00ab51c8aae729cce29a5427b8f80e28fcd5163a6f070c654ef05b85671fd1c9a2a2e52e8737b3cc04d6e95e0690b5e7d711b23c45a065f66193ab6d070a23efa56654f9427ec05e4e32a79e43869db3f3b61ca5c4fed04f2926e4bdb99367b15068868d0fb232359ea3c9e9c64150b4058a592b59943a7e23b69813bf08e637cca4ea1e5e996613a1db2684386a5e5f0dcbbea04b312f69512bab31db7e9ef94b6905a79690193f7289851821d135974af3696a2e0439585a960b3d8a31568bca8de1271a14cd30ed1a7b85adf23f9d76f5b62149242296677fea61288a4b58d2e38889b2bbd9926672df36b46fd6463bf0c0f7b1db1a7312071ef8e39a5a7deea4e47b6cda985de2cc5133337633b3db6d0cb54d56795a8a76fef2edf93debc3cbea6d0b902a0c710e73fa443c4842ec1646b4934514d2700eaa48b1320f0477f992ac0849bfd1fa628fd2b85a8997c5c2128bcb8135718b62180ede1ee8d4bf375c053a5419173b6adf54ab6f5943a377f7b2b4b81807fba3460b6acd28ef6d4be2bb015d9d5ea9b3c2d22389d7c688dd871e0a9fe295389711bf19804eeb68a86204fed691fe7493db9e0d2bce3f6ae4df5debe4cd4b4c668111146dc82b638080da9fa26d47d00f37538ee2a4a2930d39d1f734537a79d0d34f6cfab7893e2ebe71ec110e82f63a7d5c246737acadf1fc75c777faaa150164fc204c7ad3261fc9563a1b5bac908905d53fb7f37060dc1c5750634b200b31fbfaf4de1fefbc687b4c808968065e9cb567f003e1bd06b77e6442a7e5d3f54ff98d12d03d3f8705fdeb9614764acd153fc15a8763e5dc95e2c19ce478b8da93744680f2bfca75e50bebabc0cbb609575bb1000141dc35da3f4b5f5141899ff9ef856b7904baf5a828d2e4f576a58e505c5c9f3e5bc0e1ecfaf4ef36a7dee842aaf155db65912cd8dfccd11b188f5da5ea10035d47f17e0996791b0aacefa38799708198e4780846c7b1cf60aa9172a3a5935338bdb71e33a20b96181e487dd5caeeeff0446bc945ac2a83c3ca0ded82939d314073711c2afce8cc2a94fc6d46e28ea5bbafcd6adb517dedc9fa99446be727718bc41122214b6b788641e785ba0bee6530c61f9a9a58c14c6a5c477fd6f2117c67b1a2d8ca20e3aaef5c2d4e1f2b525fc8516ac90654880e72b4d87916e76592691d154a89f4125db6d9635e553955797f77ec00797f130f43b6245bf97cb88c93a641c78cbc352cf4816b075c81133cff364f4120399e3beb8cb6aae362a33de38c0d14d919a1939c00d46cd7ec2368620378fa490dd7d746361df3ee49b19a848035f619b08a66271fd97cb805ec834d85355495f003e39b6284b1ff52d5fbfeca7f7676f2a780c0b649abe6574da5867b2949af7d01e33fb9ffdee
</service>
<service md5="ae9965260e1098a43543a9a79fa1dac4" checkout="true" status="wait-confirm">
<service md5="ae9965260e1098a43543a9a79fa1dac4" checkout="true" status="verified">
2fe9827b8286e6e5329cc9af2bb910f78b57ec5d073e6ef061360d7103f1c38245e04f804f431da3b51887a6e16cfee2a69534f4ab8ded57b74029beba7ccd2347b962d8f0d2d8bc93771a52ef4688fdf9759d33be9ba18b306a86fd3848b8ca8fe61abdd9ea2b9018c31554122172488166517e67276384b23239f02f4b5c4d8290dc542f0af54a66874a34bb8cd6114d7d5bce7404294f2c41ec20b85b942e5dca6e7b94fdea2e3dcd98777d0d7b631246c2222b69bd0547dc56d4c7990e0f803c32593cd218f4d655998ddd90e488a5d12aa7ef868f7641dc6a6df0bb0abfc0e76335ab682d909d117d863c90f6e21ebdb3ff8a7126bde4b14c524afe31fad20d1b000356cd25ab2276a2adf82638a5bf20304e2ad1a2f28cd7490e818fbec3566514cdeb412ce63d421738b038e40f70073d50352307de058b40d6215f6af1ce09cf6b954be69c7ef0f8030449a20faa5c2dd9b8d0f1adf911371156dc65b470fb02a092975af2f9080f689dd56cc3cd0f4c0434537e32b80cc22bd19743b1145d713422fed681b3bb9a09c5e5ec8f739631a8ffe2a49cebb2f13634b9414da48845948473616f18814447f63e018015b3d385cc592cc80c1f5a54d04029766572be94991d3e96b952961813e35fdc3f86a2d5fbfcd48a12d531be0f68ffcf48f1eb0e14623a6cfbdea026dd766fab9bbbc442468e6559d934959f0643c963c0799fcc3f98ccfa38b84cfb962a77cf5008037c81934425f0d7f4d1b7093adfc109918a7a130f1b3d5073a1517ac1c5b6dfe962917c8704e0d94d68795355f4ac341f84124bddd270c1da1a180ca101fb5f0bd4204e11461dce54d96827dcaa3e3a3889a621734268cf27ac18fe8c0df23108f3ef949fc68fd54122f3ea5fd5b2131b1ab7264006d730a7c4b90493b6858d78f5dfcf2cddbe8f1aa2b5da1de0580862ae16e01c5bce21a537ce0a8dfec8379c5152c0a3af41e09b7d35ed8920ad0f01abf1495b6e3e565cc8734ecf22a839212626f8f46cee5b90a742d43c2de2a280a2b1ffbe7e8285d8632bbf1137c28590f0420b3e75809c71f8b0e9bd8ed8995ba81003386d9c206ff5d3db014353d9565d1ac859f9335146cd3d2c8a04b4c3a1e7fa2e37a067ea2cb8fd4826010ecc5915114301fd7b2c7c72a222618c424797fe690cc9af7bc0a609c17a1dc8de4603df8ecbcc08d09bdd382b440fb29d11c822319b8572727592915fdcc1d9f7f3a025bec58834d5c55505c8013a5daf7f6e312b509500c2adc8e664c56031666124ab3f997185ae4f9286e7e6760ca8a106484fc68e50da961819fce231fca20330729b80be767c75006b1f8b796d60da0636d8fec8bd4c69671a8681f11f21f2b104ab4961c50b50f6b2b5fc59f78b674de891652d91f32ba60deaabf554158a38989438b67fb1713a14b4a2b58843a8b2df832d0969309f23ce4e07504a6be8aba05d8d79af93f41cf70fe0ef7b20ad603e4bd4c9261b0c3a007b46663c7b16ad77e8b8029d4d76fc4941205134d4cc9b7b7f9cc961ce5651afe8b308fa982cfdd57e9c21e1c65b2e906001f98f63cea267c44032c28348655541242b2092bfe8cb521bc491d376d989baa75c8f3c588089956f0736a71245c7acf0654dbaaad04803f8d8096d9a2c786a4c96ab99e4bc38119171839b718cffba4f8503d624cdfc2209597505da520878e2a10a9a7b0cd1e54ca09cf8c1ec9898735ea38e0886c940d8390d4581d205e4d9eb03d280071c6428bcee962122094af10e3784150651c5c4a583d8814c9e14810e021c659bf8cb66f82141d9a87248b20cba5cc212b2c0cd6dd085e690e549b31376babf1f3b19581956e26b1107f198975502
</service>
<service md5="7c72644fd27fad50a9a932107bbf9321" checkout="true" status="wait-confirm">
<service md5="7c72644fd27fad50a9a932107bbf9321" checkout="true" status="verified">
74f4e96bbc0f84de1f5f481c7e3257243edfcec173663557469a90ef23c067b8606774b867568db7dfe5209aada24cc96fcf93faa7a95180ffe2a20154898e82b27e27d01358c42c1857f9bdcb4a83ee1709226e2fc117c3a93cd3cb139777fc9295b4602c5d7c76f5a47d2f574009959a274f49d36e7435ed61e755768551859dfc34277df224c2dc9365112b9459e26235e7ce8619d0d1457954de93f604a200b0e9368b53aa7a3313b4f020d28625c646b6ca7bc6feadc7297b5a51f7eeb77adb016f346ca3bd2f4878a92dcef4abad57b0e5c8a10087f717e813892f55ed4e248cc6bdf0ea1edcaa1d42adbec7d2832c184d2185fdb66f0ee0043ce0a6035a151b22d81eafcf4cf9a8ce962e6ce1f6571b6df0a86dc221b770974f5bbfe960efbfb8c148f590584fbefae0b5cb95545bf69e4e5c9a39b431155649468ef093127aba424654ee0bb46d194ad17e61b022c43671dfef0eae98ec6ba6cd2924279fb592ac1d3babb61c0befddf6521efc38d4d75007499864cced0c8ee14cb5fd37b79626f636753b585ccfcf583ee92784850f0cacd3389c3546827bf3e7ef2719b25819f0208dcf7c3493edfc048a6fa3d35511e6f9c2a367cb87ad74c0f41bf853b6ba35f51fadfd5632012bde0180585dff6a7f114a2a958ce7ecbe484304dba2f138a86c4b0aad337c0047b1cb9dcd358300155d09b72d6c0ed4c53778e86fbdc7674d37a1441c1d38bf8f4c9976540090575863c41a8211c9b9450f28edd514ed8d51e3565de2fd30d6f0bf02fcfb79319ff04e7c4ad452be372fd9fe64021c8af6e566076b78f9c6d553848f9e1bf2ad3955c94470cfb8b6ba4aa81c53acc82395870ef9677b634515c4e4bd8de971292c3aa0c150516c546f8b172613dd1e314d4caf46ebd1770bb0d73b6ea2d53969
</service>
<service md5="5ca1eafd97774b352b06fe6a5f2d537a" checkout="true" status="wait-confirm">
<service md5="5ca1eafd97774b352b06fe6a5f2d537a" checkout="true" status="verified">
8305f8dd0b412a4b95ef71a44a2cb069890895668054436640b9dffd2b0db4d84e21788b2cf8d609eb95773b4ef3c926335bc93bdc4df971a95f4c4c68ee627402c8935aa619ad276a6994004babf0bb41a8ca9fd5d22e76ec68f662eeb449ad9cdbd37f78b52fb88101e86a54323be7cd4777fdab26ada1cbba1ff0b08b6f451aa45daadcf9ef35a0a24039f2b1d5ca88ad64057afdbfe7c2bba6033380208d8d7c6c5f822a5f8bf19b5ee4f16efe068bd914d2e6ca831af24d53fa330c5d8150eb52fdd29518c14ecbc9af7e68a6e5f7a866150ecccf2b598fd6ba78521d5fe815797a6430ea09d6b394abe630df9510046bc983d9c41e3213868aeaa9dd531220959dd6305a1c582bde1a3e01d9b4be5d09150c1f4a84aa35c995103e6c6e3b6bb5345759b2219a31d05626373653d8151130ba1abef2aa78494f736792c0a462f54ebd86d2a110ae27ad1ad413baca5c7c4a302252da1423e44647cd57399e92cc805a60ce43bbda7a74797be1ad0275c0b270be2f117bd9ccc07bee42e247d178b15ba233535bffbb1c641cdd9ca3d387985b7980e8aed9752dbe30313f8a4b911f0ac7d8fd9352e3be80cdfaa3fd20ac5771c9f918d456b88cd3155de13a3f71bbcc627e766a16a86cac17f83fde98a2e7742bee4144ad235e23c76459522a96812819585155b6fcb6a08dafb19ccf93de37cb6738c803f73b0876e583d47ea3c59d6aba7b168c8de113a8f04cb813847a79682574ee71f6d9a5987b5e3be7169bf3bd82fbda4f5899e31b55b8c257f989516bbc879e60a49a4c5370230b4f728dd9389a059b481ea61046230e1382046955dae636005db580ec4a0540ee382fbc74b78b00ec821a19778fc119b673496df2bc2f11639922141d4cbb8fe22833f6fb210801419ab1b43f77a2baf892055a41c57e529026d4784aad469a4d2b530e81780f2aaea6b37b44243fa0f5e60a99391c6cf2e8a0f263a38566bfa56f761684b754bb8fa95aed80511332bff4673d600b6a09852cba34faca550f0652241a9eb04ca9652c4a19c20d37d6772604c3bbd34c82fa913a1a95e33b811f33952f4618d3600165574e29c44f7f748c05f90105791906e5920b356c4a8ce133dc2a809f0e47aa0ca2537a09688d2b92d49fa6b1f25165b6d2905ea99d1d6d56b2c39b08167df0cf18ce0f593228abbdbba718f05de4700ffa54067cb01fa65d9e378c0bc88d5f4d7bc153db964920e3c0dab30c02e87d957c369c6492bba7b240c27541d80bf0d39cbe62c077bdf6b1fb252d470cc39f9e43a13a95c08788dd5fe4e62fa3b07be01496bc977c6e1fe62977a2a8b72197fb3ad812369e96e3dd2ad329f134aaa4baee81c2ebd1b2c39e55fd9afcc554633ffb04a827ac75f7b26e49bcc341ea062ba13f672cd0a1dfd79a093273a5187e3adb6ece0e460555dc872c49b95e5054ffb772a4301feecdb072d6087345814b4af54de55d413eee562be25741a041bbdcb2850281
</service>
<service md5="a35aeef398a27556cf9e08aa6e25650c" checkout="true" status="fail">
<service md5="a35aeef398a27556cf9e08aa6e25650c" checkout="true" status="verified">
8305f8dd0b412a4b95ef71a44a2cb069890895668054436640b9dffd2b0db4d84e21788b2cf8d609eb95773b4ef3c926335bc93bdc4df971a95f4c4c68ee627402c8935aa619ad276a6994004babf0bb41a8ca9fd5d22e76ec68f662eeb449ad9cdbd37f78b52fb88101e86a54323be7cd4777fdab26ada1cbba1ff0b08b6f451aa45daadcf9ef35a0a24039f2b1d5ca88ad64057afdbfe7c2bba6033380208d8d7c6c5f822a5f8bf19b5ee4f16efe068bd914d2e6ca831af24d53fa330c5d8150eb52fdd29518c14ecbc9af7e68a6e5f7a866150ecccf2b598fd6ba78521d5fe815797a6430ea09d6b394abe630df9510046bc983d9c41e3213868aeaa9dd531220959dd6305a1c582bde1a3e01d9b4be5d09150c1f4a84aa35c995103e6c6e3b6bb5345759b2219a31d05626373653d8151130ba1abef2aa78494f736792c0a462f54ebd86d2a110ae27ad1ad413baca5c7c4a302252da1423e44647cd57399e92cc805a60ce43bbda7a74797be1ad0275c0b270be2f117bd9ccc07bee42e247d178b15ba233535bffbb1c641cdd9ca3d387985b7980e8aed9752dbe30313f8a4b911f0ac7d8fd9352e3be80cdfaa3fd20ac5771c9f918d456b88cd3155de13a3f71bbcc627e766a16a86cac17f83fde98a2e7742bee4144ad235e23c76459522a96812819585155b6fcb6a08dafb19ccf93de37cb6738c803f73b0876e583d47ea3c59d6aba7b168c8de113a8f04cb813847a79682574ee71f6d9a5987b5e3be7169bf3bd82fbda4f5899e31b55b8c257f989516bbc879e60a49a4c5370230b4f728dd9389a059b481ea61046230e1382046955dae636005db580ec4a0540ee382fbc74b78b00ec821a19778fc119b673496df2bc2f11639922141d4cbb8fe22833f6fb210801419ab1b43f77a2baf892055a41c57e529026d4784aad469a4d2b530e81780f2aaea6b37b44243fa0f5e60a99391c6cf2e8a0f263a38566bfa56f761684b754bb8fa95aed80511332bff4673d600b6a09852cba34faca550f0652241a9eb04ca9652c4a19c20d37d6772604c3bbd34c82fa913a1a95e33b811f33952f4618d3600165574e29c44f7f748c05f90105791906e5920b356c4a8ce133dc2a809f0e47aa0ca2537a09688d2b92d49fa6b1f25165b6d2905ea99d1d6d56b2c39b08167df0cf18ce0f593228abbdbba718f05de4700ffa54067cb01fa65d9e378c0bc88d5f4d7bc153db964920e3c0dab30c02e87d957c369c6492bba7b240c27541d80bf0d39cbe62c077bdf6b1fb252d470cc39f9e43a13a95c08788dd5fe4e62fa3b07be01496bc977c6e1fe62977a2a8b72197fb3ad812369e96e3dd2ad329f134aaa4baee81c2ebd1b2c39e55fd9afcc554633ffb04a827ac75f7b26e49bcc341ea062ba13f672cd0a1dfd79a093273a5187e3adb6ece0e460555dc872c49b95e5054ffb772a4301feecdb072d6087345814b4af54de55d413eee562be25741a041bbdcb2850281
</service>
<service md5="af214a23bf109f45d423f0fdcad927f4" checkout="true" status="fail">
<service md5="af214a23bf109f45d423f0fdcad927f4" checkout="true" status="verified">
fb7d736ac8ee7a3a74f952551e8856a34c05d564ee0408ca0eea47a86e833b5b30a732b9d3ca954e56231908beb9022aec02c756c3e075bd9086f9ade0381b953185b4d3ed332d5c4b454abe5d2ccbeb48b473f956d543bde4f48c1978f129da7bf481b629a8da09941e5ea66d069617f6c82ac3a685b9e6cf75d8d4faf0b2ca0b52df201372c6e2d08dbcb2b724da94c733590165282e6faefd7bb89bb5be76d452f84b50a557d4b0f45192fc562f3682aba1098818ed3ddfaf72181d66f1f7b59be2b31759b66c
</service>
</unit>

View File

@ -1,60 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2><pre id="log"></pre></h2>
<h1 id="message"></h1>
<script src="/script"></script>
<script>
async function main(){
const wsjs = new MWSE({
endpoint: "ws://localhost:7707"
});
wsjs.scope(async ()=>{
let me = wsjs.peer('me');
me.disablePairAuth();
console.log(me.socketId);
let room = wsjs.room({
accessType: "public",
description: "Benim odam",
joinType: "free",
name: "M.E.",
notifyActionInvite: true,
notifyActionJoined: true,
notifyActionEjected: false,
ifexistsJoin: true
});
await room.createRoom();
room.on('message',(...args)=>{
gg.r++
gg()
});
iroom = room;
setInterval(()=>{
room.send({
type: "merhaba"
}, true)
gg.w++;
gg()
}, 200)
});
};
function gg()
{
log.innerHTML = `${gg.w} packet writed\n${gg.r} packet recaived`
}
gg.w = 0;
gg.r = 0;
/*setInterval(()=>{
window.location.reload();
}, 30000)*/
main();
</script>
</body>
</html>

View File

@ -1,401 +0,0 @@
function WebRTC()
{
this.id = null;
this.active = false;
this.connectionStatus = "";
this.iceStatus = "";
this.gatheringStatus = "";
this.signalingStatus = "";
this.rtc = new RTCPeerConnection({
iceCandidatePoolSize: 0,
iceTransportPolicy:"all",
rtcpMuxPolicy:"require",
iceServers:[{
urls: "stun:stun.l.google.com:19302"
},{
urls: "stun:stun1.l.google.com:19302"
},{
urls: "stun:stun2.l.google.com:19302"
},{
urls: "stun:stun3.l.google.com:19302"
},{
urls: "stun:stun4.l.google.com:19302"
}]
});
/**
* @type {Map<string, {stream:MediaStream?,id:string,name:string}>}
*/
this.recaivingStream = new Map();
/**
* @type {Map<string, {stream:MediaStream?,id:string,name:string}>}
*/
this.sendingStream = new Map();
this.rtc.addEventListener("connectionstatechange",(...args)=>{
this.eventConnectionState(...args);
})
this.rtc.addEventListener("icecandidate",(...args)=>{
this.eventIcecandidate(...args);
})
this.rtc.addEventListener("iceconnectionstatechange",(...args)=>{
this.eventICEConnectionState(...args);
})
this.rtc.addEventListener("icegatheringstatechange",(...args)=>{
this.eventICEGatherinState(...args);
})
this.rtc.addEventListener("negotiationneeded",(...args)=>{
this.eventNogationNeeded(...args);
})
this.rtc.addEventListener("signalingstatechange",(...args)=>{
this.eventSignalingState(...args);
})
this.rtc.addEventListener("track",(...args)=>{
this.eventTrack(...args);
})
this.rtc.addEventListener("datachannel",(...args)=>{
this.eventDatachannel(...args);
})
let events = {};
/**
* @param {Function} callback
*/
this.addEventListener = function(event,callback){
(events[event] || (events[event]=[])).push(callback);
};
this.on = this.addEventListener;
this.dispatch = async (event,...args) => {
if(events[event]) for (const callback of events[event]) {
await callback(...args)
}
}
this.emit = this.dispatch;
/**
* @type {RTCDataChannel}
*/
this.channel = null;
this.on('input',async (data)=>{
switch(data.type)
{
case "icecandidate":{
await this.rtc.addIceCandidate(new RTCIceCandidate(data.value));
break;
}
case "offer":{
await this.rtc.setRemoteDescription(new RTCSessionDescription(data.value));
let answer = await this.rtc.createAnswer({
offerToReceiveAudio: true,
offerToReceiveVideo: true
})
await this.rtc.setLocalDescription(answer);
this.send({
type: 'answer',
value: answer
});
break;
}
case "answer":{
await this.rtc.setRemoteDescription(new RTCSessionDescription(data.value))
break;
}
case "streamInfo":{
let {id,value} = data;
if(!this.recaivingStream.has(id))
{
this.recaivingStream.set(id,{
stream: null
});
};
Object.assign(this.recaivingStream.get(id), value);
this.send({
type:'streamAccept',
id
})
break;
}
case "streamRemoved":{
let {id} = data;
this.emit('stream:stopped', this.recaivingStream.get(id));
this.sendingStream.delete(id);
break;
}
case "streamAccept":{
let {id} = data;
let {stream} = this.sendingStream.get(id);
let senders = [];
for (const track of stream.getTracks()) {
senders.push(this.rtc.addTrack(track, stream));
};
stream.senders = senders;
break;
}
case "message":{
this.emit('message', data.payload);
break;
}
}
})
};
WebRTC.channels = new Map();
WebRTC.prototype.connect = function(object){
if(!this.channel)
{
this.createDefaultDataChannel();
}
};
WebRTC.prototype.sendMessage = function(object){
this.send({
type:'message',
payload: object
});
};
WebRTC.prototype.createDefaultDataChannel = function(){
let dt = this.rtc.createDataChannel(":default:",{
ordered: true
});
dt.addEventListener("open",()=>{
this.channel = dt;
console.log(...rtcLabel, this.id, dt.label + ' veri kanalııldı');
WebRTC.channels.set(this.id, this);
});
dt.addEventListener("message",({data})=>{
let pack = JSON.parse(data);
console.log(...rtcLabel, this.id, dt.label + ' P2P Pack ', pack);
this.emit('input', pack);
})
dt.addEventListener("close",()=>{
this.channel = null;
console.log(...rtcLabel, this.id, dt.label + ' veri kanalı kapandı');
})
};
WebRTC.prototype.destroy = function(){
this.active = false;
if(this.channel)
{
this.channel.close();
this.channel = null;
}
if(this.rtc)
{
this.rtc.close();
this.rtc = null;
};
this.emit('disconnected');
WebRTC.channels.delete(this.id);
}
/**
*
* @param {RTCDataChannelEvent} event
*/
WebRTC.prototype.eventDatachannel = function(event){
console.log(...rtcLabel, this.id, event.channel.label + ' veri kanalııldı');
if(event.channel.label == ':default:'){
WebRTC.channels.set(this.id, this);
this.channel = event.channel
}
event.channel.addEventListener("message",({data})=>{
let pack = JSON.parse(data);
console.log(...rtcLabel, this.id, event.channel.label + ' P2P Pack ', pack);
this.emit('input', pack);
})
event.channel.addEventListener("close",()=>{
this.channel = null;
WebRTC.channels.delete(this.id);
WebRTC.requireGC = true;
console.log(...rtcLabel, this.id, event.channel.label + ' veri kanalı kapandı');
})
};
WebRTC.requireGC = false;
setInterval(()=>{
if(WebRTC.requireGC == false) return;
let img = document.createElement("img");
img.src = window.URL.createObjectURL(new Blob([new ArrayBuffer(5e+7)]));
img.onerror = function() {
window.URL.revokeObjectURL(this.src);
img = null;
console.log("WebRTC Pool connections garbage connection microtask completed")
};
WebRTC.requireGC = false;
}, 3000)
WebRTC.prototype.send = function(object){
if(this.channel?.readyState == "open")
{
this.channel.send(JSON.stringify(object));
}else{
this.emit('output', object);
}
};
let rtcLabel = ["%cRTC","color:red;font-weight:bold"];
WebRTC.prototype.eventConnectionState = function(){
this.connectionStatus = this.rtc.connectionState;
if(this.connectionStatus == 'connected')
{
if(this.active == false)
{
this.emit('connected');
this.active = true;
}
};
if(this.connectionStatus == 'failed' || this.connectionStatus == "disconnected" || this.connectionStatus == "closed")
{
if(this.active)
{
this.destroy();
}
}
console.log(...rtcLabel, this.id, "connectionStatus", this.connectionStatus)
};
/**
*
* @param {RTCPeerConnectionIceEvent} event
*/
WebRTC.prototype.eventIcecandidate = function(event){
console.log(...rtcLabel, this.id, 'ice created');
if(event.candidate)
{
this.send({
type:'icecandidate',
value: event.candidate
})
}
};
WebRTC.prototype.eventICEConnectionState = function(){
this.iceStatus = this.rtc.iceConnectionState;
console.log(...rtcLabel, this.id, "iceStatus",this.iceStatus)
};
WebRTC.prototype.eventICEGatherinState = function(){
this.gatheringStatus = this.rtc.iceGatheringState;
console.log(...rtcLabel, this.id, "gatheringStatus",this.gatheringStatus)
};
WebRTC.prototype.eventNogationNeeded = async function(){
console.log(...rtcLabel, this.id, "requested nogation");
let offer = await this.rtc.createOffer({
iceRestart: true,
offerToReceiveAudio: true,
offerToReceiveVideo: true
});
await this.rtc.setLocalDescription(offer);
this.send({
type: 'offer',
value: offer
});
};
WebRTC.prototype.eventSignalingState = function(){
this.signalingStatus = this.rtc.signalingState;
console.log(...rtcLabel, this.id, "signalingStatus",this.signalingStatus)
};
/**
* @param {RTCTrackEvent} event
*/
WebRTC.prototype.eventTrack = function(event){
if(event.streams.length)
{
for (const stream of event.streams) {
if(this.recaivingStream.get(stream.id).stream == null)
{
this.recaivingStream.get(stream.id).stream = stream;
this.emit('stream:added', this.recaivingStream.get(stream.id));
}else{
this.recaivingStream.get(stream.id).stream = stream;
}
}
}
};
/**
* @param {MediaStream} stream
* @param {string} name
* @param {any} info
*/
WebRTC.prototype.sendStream = function(stream,name,info){
this.send({
type: 'streamInfo',
id: stream.id,
value: {
...info,
name: name
}
});
this.sendingStream.set(stream.id,{
...info,
name: name,
stream
});
};
/**
* @param {MediaStream} stream
* @param {string} name
* @param {any} info
*/
WebRTC.prototype.stopStream = function(_stream){
if(this.connectionStatus != 'connected'){
return
}
if(this.sendingStream.has(_stream.id))
{
let {stream} = this.sendingStream.get(_stream.id);
for (const track of stream.getTracks()) {
for (const RTCPSender of this.rtc.getSenders()) {
if(RTCPSender.track?.id == track.id)
{
this.rtc.removeTrack(RTCPSender);
}
}
}
this.send({
type: 'streamRemoved',
id: stream.id
});
this.sendingStream.delete(_stream.id)
}
};
/**
* @param {string} name
* @param {any} info
*/
WebRTC.prototype.stopAllStreams = function(){
if(this.connectionStatus != 'connected'){
return
}
for (const [id, {stream}] of this.sendingStream) {
for (const track of stream.getTracks()) {
for (const RTCPSender of this.rtc.getSenders()) {
if(RTCPSender.track?.id == track.id)
{
this.rtc.removeTrack(RTCPSender);
}
}
}
this.send({
type: 'streamRemoved',
id: stream.id
});
};
this.sendingStream.clear();
};
WebRTC.getCamera = async (options) => {
return navigator.mediaDevices.getUserMedia({
video: options || {
frameRate: 10,
width: 640,
height: 480
}
})
}
WebRTC.getMicrophone = async (options) => {
return navigator.mediaDevices.getUserMedia({
audio: options || {
channelCount: 1,
sampleRate: 16000,
sampleSize: 16,
volume: 1
}
})
}
WebRTC.getDisplay = async (videoOptions) => {
return navigator.mediaDevices.getDisplayMedia({
video: videoOptions || true
})
}

View File

@ -1,62 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="http://127.0.0.1:7707/script"></script>
<script>
const wsjs = new MWSE({
endpoint: "ws://127.0.0.1:7707"
});
wsjs.scope(async () => {
let me = wsjs.peer('me');
await me.info.set("name","Abdussamed");
await me.info.set("surname","ULUTAŞ");
await me.info.set("age","25");
await me.info.set("date",1);
let t = 2;
setInterval(()=>{
me.info.set("date", t);
t++;
},2000)
let room = wsjs.room({
name: "saqut.com",
description: "saqut.com try",
joinType: "free",
ifexistsJoin: true,
accessType: "private",
notifyActionInvite: false,
notifyActionJoined: true,
notifyActionEjected: true
});
await room.createRoom();
let peers = await room.fetchPeers();
for (const peer of peers) {
await peer.info.fetch();
console.log("Peer info fetched",peer.socketId,peer.info.get());
peer.on('info', (name, value) => {
console.log("Peer info changed", peer.socketId, name, value, peer.info.get());
})
}
room.on('join', async peer => {
await peer.info.fetch();
console.log("Peer info fetched",peer.socketId,peer.info.get());
peer.on('info', (name, value) => {
console.log("Peer info changed", peer.socketId, name, value, peer.info.get());
})
});
});
</script>
</body>
</html>

2189
yarn.lock

File diff suppressed because it is too large Load Diff