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 { Contact as SkypeContact } from "skype-http/dist/lib/types/contact";
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 ID_TIMEOUT = 60 * 1000;
const ID_TIMEOUT = 60000;
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 handledIds: ExpireSet<string>;
private contacts: Map<String, SkypeContact | null> = new Map();
private conversations: Map<String, skypeHttp.Conversation | null> = new Map();
constructor(
private loginUsername: string,
private password: string,
private state?: SkypeContext.Json,
) {
super();
this.handledIds = new ExpireSet(ID_TIMEOUT);
@ -38,14 +40,32 @@ export class Client extends EventEmitter {
return "8:" + this.api.context.username;
}
public get getState(): SkypeContext.Json {
return this.api.getState();
}
public async connect() {
this.api = await skypeHttp.connect({
credentials: {
username: this.loginUsername,
password: this.password,
},
verbose: true,
});
if (this.state) {
try {
this.api = await skypeHttp.connect({ state: this.state, verbose: true });
} catch (err) {
this.api = await skypeHttp.connect({
credentials: {
username: this.loginUsername,
password: this.password,
},
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) => {
if (!evt || !evt.resource) {
@ -86,6 +106,7 @@ export class Client extends EventEmitter {
this.api.on("error", (err: Error) => {
log.error("An error occured", err);
this.emit("error", err);
});
await this.api.listen();
@ -116,7 +137,7 @@ export class Client extends EventEmitter {
return this.contacts.get(fullId) || null;
}
if (hasStart) {
id = id.substr(id.indexOf(":")+1);
id = id.substr(id.indexOf(":") + 1);
}
try {
const rawContact = await this.api.getContact(id);
@ -177,7 +198,7 @@ export class Client extends EventEmitter {
}
return await Util.DownloadFile(url, {
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(
conversationId: string,
opts: SkypeNewMediaMessage
opts: SkypeNewMediaMessage,
): Promise<skypeHttp.Api.SendMessageResult> {
return await this.api.sendDocument(opts, conversationId);
}
public async sendImage(
conversationId: string,
opts: SkypeNewMediaMessage
opts: SkypeNewMediaMessage,
): Promise<skypeHttp.Api.SendMessageResult> {
return await this.api.sendImage(opts, conversationId);
}

View File

@ -61,7 +61,6 @@ const protocol: IProtocolInformation = {
image: true,
audio: true,
file: true,
// presence: true, // we want to be able to send presence
edit: true,
globalNamespace: true,
},
@ -101,7 +100,17 @@ async function run() {
puppet.on("file", skype.handleMatrixFile.bind(skype));
puppet.setCreateUserHook(skype.createUser.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.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> => {
const retData = {
success: false,

View File

@ -12,7 +12,7 @@ limitations under the License.
*/
import {
PuppetBridge, IRemoteUser, IRemoteRoom, IReceiveParams, IMessageEvent, IFileEvent, Log, MessageDeduplicator, Util,
ExpireSet,
ExpireSet, IRetList,
} from "mx-puppet-bridge";
import { Client } from "./client";
import * as skypeHttp from "skype-http";
@ -23,6 +23,8 @@ import * as escapeHtml from "escape-html";
const log = new Log("SkypePuppet:skype");
const ROOM_TYPE_DM = 8;
interface ISkypePuppet {
client: Client;
data: any;
@ -53,12 +55,11 @@ export class Skype {
public getRoomParams(puppetId: number, conversation: skypeHttp.Conversation): IRemoteRoom {
const roomType = Number(conversation.id.split(":")[0]);
let roomId = conversation.id;
const isDirect = roomType === 8;
const isDirect = roomType === ROOM_TYPE_DM;
if (isDirect) {
return {
puppetId,
roomId: `dm-${puppetId}-${roomId}`,
roomId: `dm-${puppetId}-${conversation.id}`,
isDirect: true,
};
}
@ -76,7 +77,7 @@ export class Skype {
}
return {
puppetId,
roomId,
roomId: conversation.id,
name,
avatarUrl,
};
@ -84,7 +85,6 @@ export class Skype {
public async getSendParams(puppetId: number, resource: skypeHttp.resources.Resource): Promise<IReceiveParams | null> {
const roomType = Number(resource.conversation.split(":")[0]);
let roomId = resource.conversation;
const p = this.puppets[puppetId];
const contact = await p.client.getContact(resource.from.raw);
const conversation = await p.client.getConversation({
@ -95,18 +95,26 @@ export class Skype {
return null;
}
return {
user: await this.getUserParams(puppetId, contact),
room: await this.getRoomParams(puppetId, conversation),
user: this.getUserParams(puppetId, contact),
room: this.getRoomParams(puppetId, conversation),
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) {
const p = this.puppets[puppetId];
if (!p) {
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;
client.on("text", async (resource: skypeHttp.resources.TextResource) => {
try {
@ -124,7 +132,7 @@ export class Skype {
});
client.on("location", async (resource: skypeHttp.resources.RichTextLocationResource) => {
try {
} catch (err) {
log.error("Error while handling location event", err);
}
@ -150,8 +158,28 @@ export class Skype {
log.error("Error while handling presence event", err);
}
});
await client.connect();
await this.puppet.setUserId(puppetId, client.username);
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 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) {
@ -203,6 +231,54 @@ export class Skype {
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> {
const p = this.puppets[room.puppetId];
if (!p) {