state handling

This commit is contained in:
Sorunome 2020-03-24 22:18:38 +01:00
parent 5bc2c64859
commit 251775e93f
No known key found for this signature in database
GPG Key ID: B19471D07FC9BE9C
3 changed files with 133 additions and 27 deletions

View File

@ -16,19 +16,21 @@ import { EventEmitter } from "events";
import * as skypeHttp from "skype-http"; import * as skypeHttp from "skype-http";
import { Contact as SkypeContact } from "skype-http/dist/lib/types/contact"; import { Contact as SkypeContact } from "skype-http/dist/lib/types/contact";
import { NewMediaMessage as SkypeNewMediaMessage } from "skype-http/dist/lib/interfaces/api/api"; import { NewMediaMessage as SkypeNewMediaMessage } from "skype-http/dist/lib/interfaces/api/api";
import { Context as SkypeContext } from "skype-http/dist/lib/interfaces/api/context";
const log = new Log("SkypePuppet:client"); const log = new Log("SkypePuppet:client");
const ID_TIMEOUT = 60 * 1000; const ID_TIMEOUT = 60000;
export class Client extends EventEmitter { export class Client extends EventEmitter {
public contacts: Map<string, SkypeContact | null> = new Map();
public conversations: Map<string, skypeHttp.Conversation | null> = new Map();
private api: skypeHttp.Api; private api: skypeHttp.Api;
private handledIds: ExpireSet<string>; private handledIds: ExpireSet<string>;
private contacts: Map<String, SkypeContact | null> = new Map();
private conversations: Map<String, skypeHttp.Conversation | null> = new Map();
constructor( constructor(
private loginUsername: string, private loginUsername: string,
private password: string, private password: string,
private state?: SkypeContext.Json,
) { ) {
super(); super();
this.handledIds = new ExpireSet(ID_TIMEOUT); this.handledIds = new ExpireSet(ID_TIMEOUT);
@ -38,7 +40,15 @@ export class Client extends EventEmitter {
return "8:" + this.api.context.username; return "8:" + this.api.context.username;
} }
public get getState(): SkypeContext.Json {
return this.api.getState();
}
public async connect() { public async connect() {
if (this.state) {
try {
this.api = await skypeHttp.connect({ state: this.state, verbose: true });
} catch (err) {
this.api = await skypeHttp.connect({ this.api = await skypeHttp.connect({
credentials: { credentials: {
username: this.loginUsername, username: this.loginUsername,
@ -46,6 +56,16 @@ export class Client extends EventEmitter {
}, },
verbose: true, verbose: true,
}); });
}
} else {
this.api = await skypeHttp.connect({
credentials: {
username: this.loginUsername,
password: this.password,
},
verbose: true,
});
}
this.api.on("event", (evt: skypeHttp.events.EventMessage) => { this.api.on("event", (evt: skypeHttp.events.EventMessage) => {
if (!evt || !evt.resource) { if (!evt || !evt.resource) {
@ -86,6 +106,7 @@ export class Client extends EventEmitter {
this.api.on("error", (err: Error) => { this.api.on("error", (err: Error) => {
log.error("An error occured", err); log.error("An error occured", err);
this.emit("error", err);
}); });
await this.api.listen(); await this.api.listen();
@ -116,7 +137,7 @@ export class Client extends EventEmitter {
return this.contacts.get(fullId) || null; return this.contacts.get(fullId) || null;
} }
if (hasStart) { if (hasStart) {
id = id.substr(id.indexOf(":")+1); id = id.substr(id.indexOf(":") + 1);
} }
try { try {
const rawContact = await this.api.getContact(id); const rawContact = await this.api.getContact(id);
@ -177,7 +198,7 @@ export class Client extends EventEmitter {
} }
return await Util.DownloadFile(url, { return await Util.DownloadFile(url, {
cookies: this.api.context.cookies, cookies: this.api.context.cookies,
headers: { Authorization: 'skype_token ' + this.api.context.skypeToken.value }, headers: { Authorization: "skype_token " + this.api.context.skypeToken.value },
}); });
} }
@ -206,14 +227,14 @@ export class Client extends EventEmitter {
public async sendDocument( public async sendDocument(
conversationId: string, conversationId: string,
opts: SkypeNewMediaMessage opts: SkypeNewMediaMessage,
): Promise<skypeHttp.Api.SendMessageResult> { ): Promise<skypeHttp.Api.SendMessageResult> {
return await this.api.sendDocument(opts, conversationId); return await this.api.sendDocument(opts, conversationId);
} }
public async sendImage( public async sendImage(
conversationId: string, conversationId: string,
opts: SkypeNewMediaMessage opts: SkypeNewMediaMessage,
): Promise<skypeHttp.Api.SendMessageResult> { ): Promise<skypeHttp.Api.SendMessageResult> {
return await this.api.sendImage(opts, conversationId); return await this.api.sendImage(opts, conversationId);
} }

View File

@ -61,7 +61,6 @@ const protocol: IProtocolInformation = {
image: true, image: true,
audio: true, audio: true,
file: true, file: true,
// presence: true, // we want to be able to send presence
edit: true, edit: true,
globalNamespace: true, globalNamespace: true,
}, },
@ -101,7 +100,17 @@ async function run() {
puppet.on("file", skype.handleMatrixFile.bind(skype)); puppet.on("file", skype.handleMatrixFile.bind(skype));
puppet.setCreateUserHook(skype.createUser.bind(skype)); puppet.setCreateUserHook(skype.createUser.bind(skype));
puppet.setCreateRoomHook(skype.createRoom.bind(skype)); puppet.setCreateRoomHook(skype.createRoom.bind(skype));
puppet.setGetDmRoomIdHook(skype.getDmRoom.bind(skype));
puppet.setListUsersHook(skype.listUsers.bind(skype));
puppet.setListRoomsHook(skype.listRooms.bind(skype));
puppet.setGetUserIdsInRoomHook(skype.getUserIdsInRoom.bind(skype)); puppet.setGetUserIdsInRoomHook(skype.getUserIdsInRoom.bind(skype));
puppet.setGetDescHook(async (puppetId: number, data: any): Promise<string> => {
let s = "Skype";
if (data.username) {
s += ` as \`${data.username.substr("8:".length)}\``;
}
return s;
});
puppet.setGetDataFromStrHook(async (str: string): Promise<IRetData> => { puppet.setGetDataFromStrHook(async (str: string): Promise<IRetData> => {
const retData = { const retData = {
success: false, success: false,

View File

@ -12,7 +12,7 @@ limitations under the License.
*/ */
import { import {
PuppetBridge, IRemoteUser, IRemoteRoom, IReceiveParams, IMessageEvent, IFileEvent, Log, MessageDeduplicator, Util, PuppetBridge, IRemoteUser, IRemoteRoom, IReceiveParams, IMessageEvent, IFileEvent, Log, MessageDeduplicator, Util,
ExpireSet, ExpireSet, IRetList,
} from "mx-puppet-bridge"; } from "mx-puppet-bridge";
import { Client } from "./client"; import { Client } from "./client";
import * as skypeHttp from "skype-http"; import * as skypeHttp from "skype-http";
@ -23,6 +23,8 @@ import * as escapeHtml from "escape-html";
const log = new Log("SkypePuppet:skype"); const log = new Log("SkypePuppet:skype");
const ROOM_TYPE_DM = 8;
interface ISkypePuppet { interface ISkypePuppet {
client: Client; client: Client;
data: any; data: any;
@ -53,12 +55,11 @@ export class Skype {
public getRoomParams(puppetId: number, conversation: skypeHttp.Conversation): IRemoteRoom { public getRoomParams(puppetId: number, conversation: skypeHttp.Conversation): IRemoteRoom {
const roomType = Number(conversation.id.split(":")[0]); const roomType = Number(conversation.id.split(":")[0]);
let roomId = conversation.id; const isDirect = roomType === ROOM_TYPE_DM;
const isDirect = roomType === 8;
if (isDirect) { if (isDirect) {
return { return {
puppetId, puppetId,
roomId: `dm-${puppetId}-${roomId}`, roomId: `dm-${puppetId}-${conversation.id}`,
isDirect: true, isDirect: true,
}; };
} }
@ -76,7 +77,7 @@ export class Skype {
} }
return { return {
puppetId, puppetId,
roomId, roomId: conversation.id,
name, name,
avatarUrl, avatarUrl,
}; };
@ -84,7 +85,6 @@ export class Skype {
public async getSendParams(puppetId: number, resource: skypeHttp.resources.Resource): Promise<IReceiveParams | null> { public async getSendParams(puppetId: number, resource: skypeHttp.resources.Resource): Promise<IReceiveParams | null> {
const roomType = Number(resource.conversation.split(":")[0]); const roomType = Number(resource.conversation.split(":")[0]);
let roomId = resource.conversation;
const p = this.puppets[puppetId]; const p = this.puppets[puppetId];
const contact = await p.client.getContact(resource.from.raw); const contact = await p.client.getContact(resource.from.raw);
const conversation = await p.client.getConversation({ const conversation = await p.client.getConversation({
@ -95,18 +95,26 @@ export class Skype {
return null; return null;
} }
return { return {
user: await this.getUserParams(puppetId, contact), user: this.getUserParams(puppetId, contact),
room: await this.getRoomParams(puppetId, conversation), room: this.getRoomParams(puppetId, conversation),
eventId: (resource as any).clientId || resource.native.clientmessageid || resource.id, // tslint:disable-line no-any eventId: (resource as any).clientId || resource.native.clientmessageid || resource.id, // tslint:disable-line no-any
}; };
} }
public async stopClient(puppetId: number) {
const p = this.puppets[puppetId];
if (!p) {
return;
}
await p.client.disconnect();
}
public async startClient(puppetId: number) { public async startClient(puppetId: number) {
const p = this.puppets[puppetId]; const p = this.puppets[puppetId];
if (!p) { if (!p) {
return; return;
} }
p.client = new Client(p.data.username, p.data.password); p.client = new Client(p.data.username, p.data.password, p.data.state);
const client = p.client; const client = p.client;
client.on("text", async (resource: skypeHttp.resources.TextResource) => { client.on("text", async (resource: skypeHttp.resources.TextResource) => {
try { try {
@ -150,8 +158,28 @@ export class Skype {
log.error("Error while handling presence event", err); log.error("Error while handling presence event", err);
} }
}); });
const MINUTE = 60000;
client.on("error", async (err: Error) => {
await this.puppet.sendStatusMessage(puppetId, "Error:" + err);
await this.puppet.sendStatusMessage(puppetId, "Reconnecting in a minute... " + err.message);
setTimeout(async () => {
await this.stopClient(puppetId);
await this.startClient(puppetId);
}, MINUTE);
});
try {
await client.connect(); await client.connect();
await this.puppet.setUserId(puppetId, client.username); await this.puppet.setUserId(puppetId, client.username);
p.data.state = client.getState;
await this.puppet.setPuppetData(puppetId, p.data);
await this.puppet.sendStatusMessage(puppetId, "connected");
} catch (err) {
log.error("Failed to connect", err);
await this.puppet.sendStatusMessage(puppetId, "Failed to connect, reconnecting in a minute... " + err.message);
setTimeout(async () => {
await this.startClient(puppetId);
}, MINUTE);
}
} }
public async newPuppet(puppetId: number, data: any) { public async newPuppet(puppetId: number, data: any) {
@ -203,6 +231,54 @@ export class Skype {
return this.getRoomParams(room.puppetId, conversation); return this.getRoomParams(room.puppetId, conversation);
} }
public async getDmRoom(remoteUser: IRemoteUser): Promise<string | null> {
const p = this.puppets[remoteUser.puppetId];
if (!p) {
return null;
}
const contact = await p.client.getContact(remoteUser.userId);
if (!contact) {
return null;
}
return `dm-${remoteUser.puppetId}-${contact.mri}`;
}
public async listUsers(puppetId: number): Promise<IRetList[]> {
const p = this.puppets[puppetId];
if (!p) {
return [];
}
const reply: IRetList[] = [];
for (const [, contact] of p.client.contacts) {
if (!contact) {
continue;
}
reply.push({
id: contact.mri,
name: contact.displayName,
});
}
return reply;
}
public async listRooms(puppetId: number): Promise<IRetList[]> {
const p = this.puppets[puppetId];
if (!p) {
return [];
}
const reply: IRetList[] = [];
for (const [, conversation] of p.client.conversations) {
if (!conversation || conversation.id.startsWith("8:")) {
continue;
}
reply.push({
id: conversation.id,
name: (conversation.threadProperties && conversation.threadProperties.topic) || "",
});
}
return reply;
}
public async getUserIdsInRoom(room: IRemoteRoom): Promise<Set<string> | null> { public async getUserIdsInRoom(room: IRemoteRoom): Promise<Set<string> | null> {
const p = this.puppets[room.puppetId]; const p = this.puppets[room.puppetId];
if (!p) { if (!p) {