mirror of
https://github.com/icewind1991/mx-puppet-steam.git
synced 2026-06-03 17:44:09 +02:00
support bridging emotes
This commit is contained in:
parent
eb22f3ad87
commit
09ea312a47
7 changed files with 341 additions and 149 deletions
|
|
@ -16,10 +16,6 @@ export interface IIncomingFriendMessage {
|
|||
|
||||
export type BBCodeField = BBCodeNode | string;
|
||||
|
||||
export function isBBCode(field: BBCodeField): field is BBCodeNode {
|
||||
return field['tag'] !== undefined;
|
||||
}
|
||||
|
||||
export interface BBCodeNode {
|
||||
tag: string
|
||||
attrs: { [attr: string]: string },
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export interface LoginToken {
|
|||
steamID: SteamID
|
||||
}
|
||||
|
||||
// export async function login(details: LoginDetails, steamGuard?: () => Promise<string>, twoFactor?: () => Promise<string>, captcha?: (url: string) => Promise<string>): Promise<LoginToken> {
|
||||
// export async function login(details: LoginDetails, steamGuard?: () => Promise<string>, twoFactor?: () => Promise<string>, captcha?: (urlOrBuffer: string) => Promise<string>): Promise<LoginToken> {
|
||||
// let community = new SteamCommunity();
|
||||
// while (true) {
|
||||
// try {
|
||||
|
|
|
|||
123
src/message.ts
Normal file
123
src/message.ts
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
import {BBCodeField, BBCodeNode, IIncomingChatMessage, IIncomingFriendMessage} from "./interfaces";
|
||||
import {Steam} from "./steam";
|
||||
import UPNG from "@pdf-lib/upng";
|
||||
|
||||
const GIFEncoder = require("gif-encoder");
|
||||
import {WritableStreamBuffer} from 'stream-buffers';
|
||||
import {Util} from "mx-puppet-bridge";
|
||||
|
||||
function isBBCode(field: BBCodeField): field is BBCodeNode {
|
||||
return field['tag'] !== undefined;
|
||||
}
|
||||
|
||||
async function apngToGif(sourceUrl: string): Promise<Buffer> {
|
||||
const input = await Util.DownloadFile(sourceUrl);
|
||||
|
||||
const pngImage = UPNG.decode(input);
|
||||
let frameData = UPNG.toRGBA8(pngImage);
|
||||
|
||||
const encoder = new GIFEncoder(pngImage.width, pngImage.height);
|
||||
let output = new WritableStreamBuffer();
|
||||
encoder.pipe(output);
|
||||
|
||||
encoder.setRepeat(0);
|
||||
encoder.setTransparent(0xFF00FF);
|
||||
encoder.writeHeader();
|
||||
|
||||
for (let i = 0; i < pngImage.frames.length; i++) {
|
||||
let frame = pngImage.frames[i];
|
||||
let data = new Uint8Array(frameData[i]);
|
||||
|
||||
// set transparent pixels to 0xFF00FF
|
||||
for (let y = 0; y < data.length; y += 4) {
|
||||
if (data[y + 3] < 200) {
|
||||
data[y] = 0xFF;
|
||||
data[y + 1] = 0x00;
|
||||
data[y + 2] = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
encoder.setDelay(frame.delay);
|
||||
|
||||
encoder.addFrame(data);
|
||||
}
|
||||
|
||||
return output.getContents();
|
||||
}
|
||||
|
||||
|
||||
async function formatBBCode(steam: Steam, puppetId: number, node: BBCodeNode): Promise<ImageMessage | TextMessage> {
|
||||
if (node.tag === 'img') {
|
||||
return {
|
||||
kind: "image",
|
||||
urlOrBuffer: node.attrs['src']
|
||||
};
|
||||
} else if (node.tag === 'emoticon') {
|
||||
let emote = node.content[0] as string;
|
||||
const mxc = await steam.getEmojiMxc(
|
||||
puppetId, 'emoticonlarge', emote,
|
||||
);
|
||||
return {
|
||||
kind: "text",
|
||||
body: `:${emote}:`,
|
||||
formattedBody: `<img alt=":${emote}:" title=":${emote}:" height="32" src="${mxc}" data-mx-emoticon />`
|
||||
};
|
||||
} else if (node.tag === 'sticker') {
|
||||
let sticker = node.attrs['type'];
|
||||
let gif = await apngToGif(`https://community.cloudflare.steamstatic.com/economy/sticker/${sticker}`);
|
||||
return {
|
||||
kind: "image",
|
||||
urlOrBuffer: gif
|
||||
};
|
||||
} else {
|
||||
return {kind: "text", body: `[${node.tag}]`};
|
||||
}
|
||||
}
|
||||
|
||||
export async function exportMessageForSending(steam: Steam, puppetId: number, message: IIncomingFriendMessage | IIncomingChatMessage): Promise<(TextMessage | ImageMessage)[]> {
|
||||
if (message.message_bbcode_parsed) {
|
||||
let parts = await Promise.all(message.message_bbcode_parsed.map(node => {
|
||||
if (isBBCode(node)) {
|
||||
return formatBBCode(steam, puppetId, node);
|
||||
} else {
|
||||
return {kind: "text", body: node} as TextMessage;
|
||||
}
|
||||
}));
|
||||
|
||||
return parts.reduce((merged, part) => {
|
||||
if (merged.length === 0) {
|
||||
merged.push(part);
|
||||
} else {
|
||||
let last = merged[merged.length - 1];
|
||||
|
||||
// merge adjacent text nodes
|
||||
if (last.kind === "text" && part.kind === "text") {
|
||||
if (!last.formattedBody) {
|
||||
last.formattedBody = last.body;
|
||||
}
|
||||
if (!part.formattedBody) {
|
||||
part.formattedBody = part.body;
|
||||
}
|
||||
last.body += " " + part.body;
|
||||
last.formattedBody += " " + part.formattedBody;
|
||||
} else {
|
||||
merged.push(part);
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
}, [] as (TextMessage | ImageMessage)[]);
|
||||
} else {
|
||||
return [{kind: "text", body: message.message_no_bbcode}];
|
||||
}
|
||||
}
|
||||
|
||||
export interface TextMessage {
|
||||
kind: "text";
|
||||
body: string;
|
||||
formattedBody?: string;
|
||||
}
|
||||
|
||||
export interface ImageMessage {
|
||||
kind: "image";
|
||||
urlOrBuffer: string | Buffer
|
||||
}
|
||||
71
src/steam.ts
71
src/steam.ts
|
|
@ -20,10 +20,10 @@ import {
|
|||
IGroupDetails,
|
||||
IIncomingChatMessage,
|
||||
IIncomingFriendMessage,
|
||||
IPersona,
|
||||
isBBCode
|
||||
IPersona
|
||||
} from "./interfaces";
|
||||
import {debounce} from 'ts-debounce';
|
||||
import {exportMessageForSending} from "./message";
|
||||
|
||||
const log = new Log("MatrixPuppet:Steam");
|
||||
|
||||
|
|
@ -284,7 +284,7 @@ export class Steam {
|
|||
|
||||
let sendParams = await this.getFriendMessageSendParams(puppetId, message, fromSteamId);
|
||||
|
||||
await this.sendMessage(p, sendParams, message);
|
||||
await this.sendMessage(p, puppetId, sendParams, message);
|
||||
}
|
||||
|
||||
public async handleChatMessage(puppetId: number, message: IIncomingChatMessage, fromSteamId?: SteamID) {
|
||||
|
|
@ -293,30 +293,28 @@ export class Steam {
|
|||
|
||||
let sendParams = await this.getChatMessageSendParams(puppetId, message, fromSteamId);
|
||||
|
||||
await this.sendMessage(p, sendParams, message);
|
||||
await this.sendMessage(p, puppetId, sendParams, message);
|
||||
}
|
||||
|
||||
public async sendMessage(puppet: ISteamPuppet, sendParams: IReceiveParams, message: IIncomingFriendMessage | IIncomingChatMessage) {
|
||||
// message is only an embedded image
|
||||
if (
|
||||
message.message_bbcode_parsed
|
||||
&& message.message_bbcode_parsed.length === 1
|
||||
&& isBBCode(message.message_bbcode_parsed[0])
|
||||
&& message.message_bbcode_parsed[0].tag === 'img'
|
||||
&& message.message_no_bbcode === message.message_bbcode_parsed[0].attrs['src']
|
||||
) {
|
||||
const url = message.message_bbcode_parsed[0].attrs['src'];
|
||||
let i = puppet.ourSendImages.indexOf(url);
|
||||
if (i === -1) {
|
||||
await this.bridge.sendImage(sendParams, url);
|
||||
} else {
|
||||
// image came from us, dont send
|
||||
puppet.ourSendImages.splice(i);
|
||||
public async sendMessage(puppet: ISteamPuppet, puppetId: number, sendParams: IReceiveParams, incoming: IIncomingFriendMessage | IIncomingChatMessage) {
|
||||
const parts = await exportMessageForSending(this, puppetId, incoming);
|
||||
|
||||
for (let part of parts) {
|
||||
if (part.kind === "image") {
|
||||
const imageUrl = part.urlOrBuffer;
|
||||
let i = (typeof imageUrl === "string") ? puppet.ourSendImages.indexOf(imageUrl) : -1;
|
||||
if (i === -1) {
|
||||
await this.bridge.sendImage(sendParams, imageUrl);
|
||||
} else {
|
||||
// image came from us, dont send
|
||||
puppet.ourSendImages.splice(i);
|
||||
}
|
||||
} else if (part.kind === "text") {
|
||||
await this.bridge.sendMessage(sendParams, {
|
||||
body: part.body,
|
||||
formattedBody: part.formattedBody,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
await this.bridge.sendMessage(sendParams, {
|
||||
body: message.message_no_bbcode,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -382,7 +380,8 @@ export class Steam {
|
|||
|
||||
let steamId = this.getRoomSteamId(room);
|
||||
if (steamId) {
|
||||
const bufferPromise = Util.DownloadFile(data.url);;
|
||||
const bufferPromise = Util.DownloadFile(data.url);
|
||||
;
|
||||
|
||||
await new Promise((resolve, _reject) => {
|
||||
p.client.webLogOn();
|
||||
|
|
@ -560,4 +559,26 @@ export class Steam {
|
|||
p.client.setPersona(EPersonaState.Away);
|
||||
}
|
||||
}
|
||||
|
||||
public async getEmojiMxc(puppetId: number, type: 'sticker' | 'emoticonlarge', name: string): Promise<string | null> {
|
||||
const id = `${type}/${name}`;
|
||||
const emoji = await this.bridge.emoteSync.get({
|
||||
puppetId,
|
||||
emoteId: id,
|
||||
});
|
||||
if (emoji && emoji.avatarMxc) {
|
||||
return emoji.avatarMxc;
|
||||
}
|
||||
const {emote} = await this.bridge.emoteSync.set({
|
||||
puppetId,
|
||||
emoteId: id,
|
||||
avatarUrl: `https://community.cloudflare.steamstatic.com/economy/${type}/${encodeURIComponent(name)}`,
|
||||
name,
|
||||
data: {
|
||||
type,
|
||||
name,
|
||||
},
|
||||
});
|
||||
return emote.avatarMxc || null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue