Skip to content
Extraits de code Groupes Projets

Comparer les révisions

Les modifications sont affichées comme si la révision source était fusionnée avec la révision cible. En savoir plus sur la comparaison des révisions.

Source

Sélectionner le projet cible
No results found
Sélectionner une révision Git
Loading items

Cible

Sélectionner le projet cible
  • arise/matrix-appservice-discord
  • Deurstann/matrix-appservice-discord
2 résultats
Sélectionner une révision Git
Loading items
Afficher les modifications
Validations sur la source (14)
......@@ -20,7 +20,7 @@ import { DiscordStore } from "./store";
import { DbEmoji } from "./db/dbdataemoji";
import { DbEvent } from "./db/dbdataevent";
import { DiscordMessageProcessor } from "./discordmessageprocessor";
import { IDiscordMessageParserResult } from "@mx-puppet/matrix-discord-parser";
import { IDiscordMessageParserResult } from "@deurstann/matrix-discord-parser";
import { MatrixEventProcessor, MatrixEventProcessorOpts, IMatrixEventProcessorResult } from "./matrixeventprocessor";
import { PresenceHandler } from "./presencehandler";
import { Provisioner } from "./provisioner";
......@@ -567,7 +567,7 @@ export class DiscordBot {
return;
}
const link = `https://discord.com/channels/${chan.guild.id}/${chan.id}/${editEventId}`;
embedSet.messageEmbed.description = `[Edit](${link}): ${embedSet.messageEmbed.description}`;
embedSet.messageEmbed.description = `[Edit](<${link}>): ${embedSet.messageEmbed.description}`;
await this.send(embedSet, opts, roomLookup, event);
} catch (err) {
// throw wrapError(err, Unstable.ForeignNetworkError, "Couldn't edit message");
......@@ -1041,65 +1041,14 @@ export class DiscordBot {
}
try {
const intent = this.GetIntentFromDiscordMember(msg.author, msg.webhookID);
// Check Attachements
if (!editEventId) {
// on discord you can't edit in images, you can only edit text
// so it is safe to only check image upload stuff if we don't have
// an edit
await Util.AsyncForEach(msg.attachments.array(), async (attachment) => {
const content = await Util.DownloadFile(attachment.url);
const fileMime = content.mimeType || mime.getType(attachment.name || "")
|| "application/octet-stream";
const mxcUrl = await intent.underlyingClient.uploadContent(
content.buffer,
fileMime,
attachment.name || "",
);
const type = fileMime.split("/")[0];
let msgtype = {
audio: "m.audio",
image: "m.image",
video: "m.video",
}[type];
if (!msgtype) {
msgtype = "m.file";
}
const info = {
mimetype: fileMime,
size: attachment.size,
} as IMatrixMediaInfo;
if (msgtype === "m.image" || msgtype === "m.video") {
info.w = attachment.width!;
info.h = attachment.height!;
}
await Util.AsyncForEach(rooms, async (room) => {
const eventId = await intent.sendEvent(room, {
body: attachment.name || "file",
external_url: attachment.url,
info,
msgtype,
url: mxcUrl,
});
this.lastEventIds[room] = eventId;
const evt = new DbEvent();
evt.MatrixId = `${eventId};${room}`;
evt.DiscordId = msg.id;
evt.ChannelId = msg.channel.id;
if (msg.guild) {
evt.GuildId = msg.guild.id;
}
await this.store.Insert(evt);
this.userActivity.updateUserActivity(intent.userId);
});
});
}
if (!msg.content && msg.embeds.length === 0) {
if (!msg.content && msg.embeds.length === 0 && msg.attachments.array().length === 0) {
return;
}
const result = await this.discordMsgProcessor.FormatMessage(msg);
if (!result.body) {
if (!result.body && msg.attachments.array().length === 0) {
return;
}
if(result.body){
await Util.AsyncForEach(rooms, async (room) => {
const sendContent: IMatrixMessage = {
body: result.body,
......@@ -1107,6 +1056,30 @@ export class DiscordBot {
formatted_body: result.formattedBody,
msgtype: result.msgtype,
};
if (msg.reference) {
const storeEvent = await this.store.Get(DbEvent, { discord_id: msg.reference?.messageID });
if (storeEvent && storeEvent.Result) {
let replyToEventId: string | undefined = undefined;
while (storeEvent.Next()) {
const [eventId] = storeEvent.MatrixId.split(";");
// Try to get the "deepest" event ID if this event replaces another ID
// We need to do this since a m.in_reply_to relation requires the original event ID and not the replacement one
const { chunk } = await intent.underlyingClient.unstableApis.getRelationsForEvent(room, eventId, "m.replace");
if (!!chunk?.length) {
replyToEventId = chunk[0].content['m.relates_to'].event_id;
} else {
replyToEventId ??= eventId;
}
}
if (replyToEventId) {
sendContent["m.relates_to"] = {
"m.in_reply_to": {
event_id: replyToEventId
}
};
}
}
}
if (editEventId) {
sendContent.body = `* ${result.body}`;
sendContent.formatted_body = `* ${result.formattedBody}`;
......@@ -1152,6 +1125,59 @@ export class DiscordBot {
await afterSend(res);
}
});
}
// Check Attachements
if (!editEventId) {
// on discord you can't edit in images, you can only edit text
// so it is safe to only check image upload stuff if we don't have
// an edit
await Util.AsyncForEach(msg.attachments.array(), async (attachment) => {
const content = await Util.DownloadFile(attachment.url);
const fileMime = content.mimeType || mime.getType(attachment.name || "")
|| "application/octet-stream";
const mxcUrl = await intent.underlyingClient.uploadContent(
content.buffer,
fileMime,
attachment.name || "",
);
const type = fileMime.split("/")[0];
let msgtype = {
audio: "m.audio",
image: "m.image",
video: "m.video",
}[type];
if (!msgtype) {
msgtype = "m.file";
}
const info = {
mimetype: fileMime,
size: attachment.size,
} as IMatrixMediaInfo;
if (msgtype === "m.image" || msgtype === "m.video") {
info.w = attachment.width!;
info.h = attachment.height!;
}
await Util.AsyncForEach(rooms, async (room) => {
const eventId = await intent.sendEvent(room, {
body: attachment.name || "file",
external_url: attachment.url,
info,
msgtype,
url: mxcUrl,
});
this.lastEventIds[room] = eventId;
const evt = new DbEvent();
evt.MatrixId = `${eventId};${room}`;
evt.DiscordId = msg.id;
evt.ChannelId = msg.channel.id;
if (msg.guild) {
evt.GuildId = msg.guild.id;
}
await this.store.Insert(evt);
this.userActivity.updateUserActivity(intent.userId);
});
});
}
MetricPeg.get.requestOutcome(msg.id, true, "success");
} catch (err) {
MetricPeg.get.requestOutcome(msg.id, true, "fail");
......@@ -1169,6 +1195,9 @@ export class DiscordBot {
if (storeEvent && storeEvent.Result) {
while (storeEvent.Next()) {
const matrixIds = storeEvent.MatrixId.split(";");
if(!oldMsg.content && oldMsg.attachments.array().length>0){
newMsg.content = newMsg.content + " " + oldMsg.attachments.array()[0].url;
}
await this.OnMessage(newMsg, matrixIds[0]);
return;
}
......
......@@ -22,7 +22,7 @@ import {
IDiscordMessageParserOpts,
IDiscordMessageParserCallbacks,
IDiscordMessageParserResult,
} from "@mx-puppet/matrix-discord-parser";
} from "@deurstann/matrix-discord-parser";
const log = new Log("DiscordMessageProcessor");
......
......@@ -72,7 +72,9 @@ export class MatrixCommandHandler {
cat: "events",
level: PROVISIONING_DEFAULT_POWER_LEVEL,
selfService: true,
subcat: "m.room.power_levels",
//subcat: "m.room.power_levels",
// use default power level (50)
subcat: "null",
},
run: async ({guildId, channelId}) => {
if (roomEntry && roomEntry.remote) {
......@@ -118,7 +120,7 @@ export class MatrixCommandHandler {
cat: "events",
level: PROVISIONING_DEFAULT_POWER_LEVEL,
selfService: true,
subcat: "m.room.power_levels",
subcat: "null",
},
run: async () => {
if (!roomEntry || !roomEntry.remote) {
......
......@@ -391,7 +391,8 @@ export class MatrixEventProcessor {
if (!sourceEvent || !sourceEvent.content || !sourceEvent.content.body) {
throw Error("No content could be found");
}
const replyEmbed = (await this.EventToEmbed(sourceEvent, channel, true)).messageEmbed;
const eventToTakeTextFrom = sourceEvent.unsigned?.["m.relations"]?.["m.replace"] ?? sourceEvent;
const replyEmbed = (await this.EventToEmbed(eventToTakeTextFrom, channel, true)).messageEmbed;
// if we reply to a discord member, ping them!
if (this.bridge.isNamespacedUser(sourceEvent.sender)) {
......@@ -533,7 +534,7 @@ export class MatrixEventProcessor {
embed.setAuthor(
displayName.substring(0, MAX_NAME_LENGTH),
avatarUrl,
`https://matrix.to/#/${sender}`,
``,
);
}
......
......@@ -24,7 +24,7 @@ import {
IMatrixMessageParserCallbacks,
IMatrixMessageParserOpts,
MatrixMessageParser,
} from "@mx-puppet/matrix-discord-parser";
} from "@deurstann/matrix-discord-parser";
const DEFAULT_ROOM_NOTIFY_POWER_LEVEL = 50;
......
......@@ -317,7 +317,7 @@ export class UserSyncroniser {
// for webhooks we append the username to the mxid, as webhooks with the same
// id can have multiple different usernames set. This way we don't spam
// userstate changes
mxidExtra = "_" + Util.ParseMxid(`@${user.username}`, false).localpart;
mxidExtra = "_" + Util.ParseMxid(`@${user.username}`).localpart;
}
const guildState: IGuildMemberState = Object.assign({}, DEFAULT_GUILD_STATE, {
bot: user.bot,
......
......@@ -365,10 +365,7 @@ export class Util {
const badChars = new Set(localpart.replace(/([a-z0-9]|-|\.|=|_)+/g, ""));
badChars.forEach((c) => {
const hex = c.charCodeAt(0).toString(RADIX).toLowerCase();
localpart = localpart.replace(
new RegExp(`\\${c}`, "g"),
`=${hex}`,
);
localpart = localpart.replaceAll(c, `=${hex}`);
});
}
return {
......@@ -380,17 +377,7 @@ export class Util {
// Taken from https://github.com/matrix-org/matrix-appservice-bridge/blob/master/lib/models/users/matrix.js
public static EscapeStringForUserId(localpart: string) {
// NOTE: Currently Matrix accepts / in the userId, although going forward it will be removed.
const badChars = new Set(localpart.replace(/([a-z]|[0-9]|-|\.|=|_)+/g, ""));
let res = localpart;
badChars.forEach((c) => {
const hex = c.charCodeAt(0).toString(16).toLowerCase();
res = res.replace(
new RegExp(`\\x${hex}`, "g"),
`=${hex}`,
);
});
return res;
return localpart.replace(/[^a-z0-9-._]/g, a => `=${a.codePointAt(0)!.toString(16).toLowerCase()}`)
}
}
......
......@@ -146,7 +146,7 @@ export class AppserviceMock extends AppserviceMockBase {
class IntentMock extends AppserviceMockBase {
public readonly underlyingClient: MatrixClientMock;
constructor(private opts: IAppserviceMockOpts = {}, private id: string) {
constructor(private opts: IAppserviceMockOpts = {}, public userId: string) {
super();
this.underlyingClient = new MatrixClientMock(opts);
}
......@@ -177,9 +177,10 @@ class IntentMock extends AppserviceMockBase {
}
class MatrixClientMock extends AppserviceMockBase {
public readonly unstableApis: UnstableApis;;
constructor(private opts: IAppserviceMockOpts = {}) {
super();
this.unstableApis = new UnstableApis();
}
public banUser(roomId: string, userId: string) {
......@@ -276,4 +277,19 @@ class MatrixClientMock extends AppserviceMockBase {
public async setPresenceStatus(presence: string, status: string) {
this.funcCalled("setPresenceStatus", presence, status);
}
public async redactEvent(roomId: string, eventId: string, reason?: string | null) {
this.funcCalled("redactEvent", roomId, eventId, reason);
}
}
class UnstableApis extends AppserviceMockBase {
public async addReactionToEvent(roomId: string, eventId: string, emoji: string) {
this.funcCalled("addReactionToEvent", roomId, eventId, emoji);
}
public async getRelationsForEvent(roomId: string, eventId: string, relationType?: string, eventType?: string): Promise<any> {
this.funcCalled("getRelationsForEvent", roomId, eventId, relationType, eventType);
}
}
......@@ -29,12 +29,21 @@ export class MockMessage {
public guild: Discord.Guild | undefined;
public author: MockUser;
public mentions: any = {};
constructor(channel?: Discord.TextChannel) {
public reference?: Discord.MessageReference = undefined;
constructor(
channel?: Discord.TextChannel,
content: string = "",
author: MockUser = new MockUser("123456"),
reference = undefined
) {
this.mentions.everyone = false;
this.channel = channel;
if (channel && channel.guild) {
this.guild = channel.guild;
}
this.author = new MockUser("123456");
this.content = content;
this.author = author;
this.reference = reference;
}
}
......@@ -16,6 +16,7 @@ limitations under the License.
import { expect } from "chai";
import * as Proxyquire from "proxyquire";
import * as Discord from "better-discord.js";
import { MockGuild } from "./mocks/guild";
import { MockMember } from "./mocks/member";
......@@ -105,8 +106,15 @@ describe("DiscordBot", () => {
describe("OnMessage()", () => {
const channel = new MockTextChannel();
const msg = new MockMessage(channel);
const author = new MockUser("11111");
const msgReply: Discord.MessageReference = {
messageID: '111',
guildID: '111',
channelID: '111',
};
const authorId = '11111';
const author = new MockUser(authorId);
let HANDLE_COMMAND = false;
const roomId = "!asdf:localhost";
function getDiscordBot() {
HANDLE_COMMAND = false;
mockBridge.cleanup();
......@@ -120,7 +128,7 @@ describe("DiscordBot", () => {
OnUpdateUser: async () => { },
};
discord.channelSync = {
GetRoomIdsFromChannel: async () => ["!asdf:localhost"],
GetRoomIdsFromChannel: async () => [roomId],
};
discord.discordCommandHandler = {
Process: async () => { HANDLE_COMMAND = true; },
......@@ -161,6 +169,89 @@ describe("DiscordBot", () => {
await discordBot.OnMessage(msg as any);
mockBridge.getIntent(author.id).wasCalled("sendEvent");
});
describe("sends replies", () => {
const originalMxId = "$mAKet_w5WYFCgh1WaHVOvyn9LJLbolFeuELTKVfm0Po";
const replacementMxId = "$grdFSE_12LFSELOIFMSOEIJOIJ98kljnfIfESJOIFESO";
msg.author = author;
msg.content = "Foxies are amazing!";
msg.reference = msgReply;
it("to a Discord message", async () => {
discordBot = getDiscordBot();
discordBot.store = {
Get: async (a, b) => {
let storeMockResults = 0;
return Promise.resolve({
Result: true,
MatrixId: `${originalMxId};${roomId}`,
Next: () => storeMockResults-- >= 0
});
},
Insert: async () => { },
};
const intent = mockBridge.getIntent(author.id);
intent.underlyingClient.unstableApis.getRelationsForEvent = async () => ({
chunk: []
});
await discordBot.OnMessage(msg);
mockBridge.getIntent(author.id).wasCalled("sendEvent", true, roomId, {
body: msg.content,
format: "org.matrix.custom.html",
formatted_body: msg.content,
msgtype: 'm.text',
"m.relates_to": {
"m.in_reply_to": {
event_id: originalMxId
}
}
});
});
it("to an edited Discord message", async () => {
let storeEventData = { MatrixId: `${originalMxId};${roomId}` };
discordBot = getDiscordBot();
discordBot.store = {
Get: async (a, b) => {
let storeMockResults = 1;
return Promise.resolve({
Result: true,
...storeEventData,
Next: () => {
storeEventData = {
MatrixId: `${replacementMxId};${roomId}`,
}
return storeMockResults-- >= 0;
}
})
},
Insert: async () => { },
}
const intent = mockBridge.getIntent(author.id);
intent.underlyingClient.unstableApis.getRelationsForEvent = async () => ({
chunk: [{
sender: "11111",
room_id: roomId,
event_id: originalMxId,
content: {
"m.relates_to": {
event_id: replacementMxId,
rel_type: 'm.replace'
}
}
}]
});
await discordBot.OnMessage(msg);
mockBridge.getIntent(author.id).wasCalled("sendEvent", true, roomId, {
body: msg.content,
format: "org.matrix.custom.html",
formatted_body: msg.content,
msgtype: 'm.text',
"m.relates_to": {
"m.in_reply_to": {
event_id: replacementMxId
}
}
});
});
});
it("sends edit messages", async () => {
discordBot = getDiscordBot();
msg.author = author;
......
......@@ -2,7 +2,7 @@
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"target": "es2016",
"target": "es2021",
"noImplicitAny": false,
"inlineSourceMap": true,
"outDir": "./build",
......
......@@ -215,6 +215,18 @@
enabled "2.0.x"
kuler "^2.0.0"
"@deurstann/matrix-discord-parser@1.10.7":
version "1.10.7"
resolved "https://registry.yarnpkg.com/@deurstann/matrix-discord-parser/-/matrix-discord-parser-1.10.7.tgz#570d0563c3f916faed877cdf6c06be0f3cf607ee"
integrity sha512-Ud/KKGkCdsDMoBW2UZNwiR5UNvkmJJJODAmsSQL/vXh9Iz7zfLqnI/cbP4nUrv1Txd5XqGi/pGSNnZ5Kd6AreQ==
dependencies:
"@mx-puppet/discord-markdown" "2.3.1"
escape-html "^1.0.3"
got "^11.6.0"
highlight.js "^10.4.1"
node-html-parser "^1.4.5"
unescape-html "^1.1.0"
"@discordjs/collection@^0.1.6":
version "0.1.6"
resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.1.6.tgz#9e9a7637f4e4e0688fd8b2b5c63133c91607682c"
......@@ -337,18 +349,6 @@
node-emoji "^1.10.0"
simple-markdown "^0.7.2"
"@mx-puppet/matrix-discord-parser@0.1.10":
version "0.1.10"
resolved "https://gitlab.com/api/v4/projects/35066311/packages/npm/@mx-puppet/matrix-discord-parser/-/@mx-puppet/matrix-discord-parser-0.1.10.tgz#0a37a3f9430ff7c29512d29882e25ae738a31283"
integrity sha1-Cjej+UMP98KVEtKYguJa5zijEoM=
dependencies:
"@mx-puppet/discord-markdown" "2.3.1"
escape-html "^1.0.3"
got "^11.6.0"
highlight.js "^10.4.1"
node-html-parser "^1.4.5"
unescape-html "^1.1.0"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
......