Telegram Web K with changes to work inside I2P https://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

168 lines
4.8 KiB

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
// #if MTPROTO_SW
import '../mtproto/mtproto.worker';
// #endif
import {logger, LogTypes} from '../logger';
import {CACHE_ASSETS_NAME, requestCache} from './cache';
import onStreamFetch from './stream';
import {closeAllNotifications, onPing} from './push';
import CacheStorageController from '../files/cacheStorage';
import {IS_SAFARI} from '../../environment/userAgent';
import ServiceMessagePort from './serviceMessagePort';
import listenMessagePort from '../../helpers/listenMessagePort';
import {getWindowClients} from '../../helpers/context';
import {MessageSendPort} from '../mtproto/superMessagePort';
import handleDownload from './download';
export const log = logger('SW', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn);
const ctx = self as any as ServiceWorkerGlobalScope;
// #if !MTPROTO_SW
let _mtprotoMessagePort: MessagePort;
export const getMtprotoMessagePort = () => _mtprotoMessagePort;
const sendMessagePort = (source: MessageSendPort) => {
const channel = new MessageChannel();
serviceMessagePort.attachPort(_mtprotoMessagePort = channel.port1);
serviceMessagePort.invokeVoid('port', undefined, source, [channel.port2]);
};
const sendMessagePortIfNeeded = (source: MessageSendPort) => {
if(!connectedWindows.size && !_mtprotoMessagePort) {
sendMessagePort(source);
}
};
const onWindowConnected = (source: WindowClient) => {
log('window connected', source.id);
if(source.frameType === 'none') {
log.warn('maybe a bugged Safari starting window', source.id);
return;
}
sendMessagePortIfNeeded(source);
connectedWindows.add(source.id);
};
export const serviceMessagePort = new ServiceMessagePort<false>();
serviceMessagePort.addMultipleEventsListeners({
notificationsClear: closeAllNotifications,
toggleStorages: ({enabled, clearWrite}) => {
CacheStorageController.toggleStorage(enabled, clearWrite);
},
pushPing: (payload, source) => {
onPing(payload, source);
},
hello: (payload, source) => {
onWindowConnected(source as any as WindowClient);
}
});
const {
onDownloadFetch,
onClosedWindows: onDownloadClosedWindows
} = handleDownload(serviceMessagePort);
// * service worker can be killed, so won't get 'hello' event
getWindowClients().then((windowClients) => {
log(`got ${windowClients.length} windows from the start`);
windowClients.forEach((windowClient) => {
onWindowConnected(windowClient);
});
});
const connectedWindows: Set<string> = new Set();
listenMessagePort(serviceMessagePort, undefined, (source) => {
const isWindowClient = source instanceof WindowClient;
if(!isWindowClient || !connectedWindows.has(source.id)) {
return;
}
log('window disconnected');
connectedWindows.delete(source.id);
if(!connectedWindows.size) {
log.warn('no windows left');
if(_mtprotoMessagePort) {
serviceMessagePort.detachPort(_mtprotoMessagePort);
_mtprotoMessagePort = undefined;
}
onDownloadClosedWindows();
}
});
// #endif
const onFetch = (event: FetchEvent): void => {
// #if !DEBUG
if(
!IS_SAFARI &&
event.request.url.indexOf(location.origin + '/') === 0 &&
event.request.url.match(/\.(js|css|jpe?g|json|wasm|png|mp3|svg|tgs|ico|woff2?|ttf|webmanifest?)(?:\?.*)?$/)
) {
return event.respondWith(requestCache(event));
}
// #endif
try {
const [, url, scope, params] = /http[:s]+\/\/.*?(\/(.*?)(?:$|\/(.*)$))/.exec(event.request.url) || [];
// log.debug('[fetch]:', event);
switch(scope) {
case 'stream': {
onStreamFetch(event, params);
break;
}
case 'download': {
onDownloadFetch(event, params);
break;
}
}
} catch(err) {
log.error('fetch error', err);
event.respondWith(new Response('', {
status: 500,
statusText: 'Internal Server Error',
headers: {'Cache-Control': 'no-cache'}
}));
}
};
const onChangeState = () => {
ctx.onfetch = onFetch;
};
ctx.addEventListener('install', (event) => {
log('installing');
event.waitUntil(ctx.skipWaiting().then(() => log('skipped waiting'))); // Activate worker immediately
});
ctx.addEventListener('activate', (event) => {
log('activating', ctx);
event.waitUntil(ctx.caches.delete(CACHE_ASSETS_NAME).then(() => log('cleared assets cache')));
event.waitUntil(ctx.clients.claim().then(() => log('claimed clients')));
});
// ctx.onerror = (error) => {
// log.error('error:', error);
// };
// ctx.onunhandledrejection = (error) => {
// log.error('onunhandledrejection:', error);
// };
ctx.onoffline = ctx.ononline = onChangeState;
onChangeState();