xmppjs-chat-bot
Version:
Server-side XMPP chat bot
267 lines • 9.38 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RoomUser = exports.Room = void 0;
const logger_1 = require("./logger");
const events_1 = __importDefault(require("events"));
const jid_1 = require("@xmpp/jid");
const xml_1 = __importDefault(require("@xmpp/xml"));
const user_1 = require("./user");
Object.defineProperty(exports, "RoomUser", { enumerable: true, get: function () { return user_1.RoomUser; } });
const xmppid = require('@xmpp/id');
class Room extends events_1.default {
constructor(bot, roomJID) {
super();
this.bot = bot;
this.roomJID = roomJID;
this.state = 'offline';
this.roster = new Map();
this.handlers = {};
this.logger = (0, logger_1.wrapLogger)(this.roomJID.toString(), bot.logger);
}
reset() {
this.state = 'offline';
this.roster.clear();
}
isOnline() {
return this.state === 'online';
}
onlineUserCount() {
let count = 0;
this.roster.forEach(user => {
if (user.isOnline()) {
count++;
}
});
return count;
}
getOnlineUsers() {
const r = [];
this.roster.forEach(user => {
if (user.isOnline()) {
r.push(user);
}
});
return r;
}
get myNick() {
return this.userJID?.getResource();
}
get jid() {
return this.roomJID;
}
async join(nick) {
this.userJID = new jid_1.JID(this.roomJID.getLocal(), this.roomJID.getDomain(), nick);
this.logger.debug(`Emitting a presence for room ${this.roomJID.toString()}...`);
await this.bot.sendStanza('presence', {
to: this.userJID.toString()
}, (0, xml_1.default)('x', {
xmlns: 'http://jabber.org/protocol/muc'
}));
}
async part() {
if (!this.userJID) {
return;
}
this.logger.debug(`Emitting a presence=unavailable for room ${this.roomJID.toString()}...`);
await this.bot.sendStanza('presence', {
to: this.userJID.toString(),
type: 'unavailable'
});
}
async changeNickname(nick) {
return this.join(nick);
}
async sendGroupchat(msg, references) {
if (!this.userJID) {
return;
}
this.logger.debug(`Emitting a groupchat message for room ${this.roomJID.toString()}...`);
const children = [];
children.push((0, xml_1.default)('body', {}, msg));
if (references) {
references.forEach(reference => {
children.push(reference.toXml());
});
}
await this.bot.sendStanza('message', {
type: 'groupchat',
to: this.roomJID.toString()
}, ...children);
}
async moderateMessage(stanza, reason) {
const messageId = stanza.uniqueAndStableStanzaID();
if (!messageId) {
this.logger.error('Can\'t emit a retract for a message without uniqueAndStableStanzaID.');
return;
}
this.logger.debug(`Emitting a retract for room ${this.roomJID.toString()} and message ${messageId}...`);
const moderateChildren = [
(0, xml_1.default)('retract', {
xmlns: 'urn:xmpp:message-retract:0'
})
];
if (reason) {
moderateChildren.push((0, xml_1.default)('reason', {}, reason));
}
const applyTo = (0, xml_1.default)('apply-to', {
xmlns: 'urn:xmpp:fasten:0',
id: messageId
}, (0, xml_1.default)('moderate', {
xmlns: 'urn:xmpp:message-moderate:0'
}, ...moderateChildren));
await this.bot.sendStanza('iq', {
type: 'set',
to: this.roomJID.toString(),
id: xmppid()
}, applyTo);
}
receiveStanza(stanza) {
try {
if (stanza.stanzaType === 'presence') {
this.receivePresenceStanza(stanza);
}
if (stanza.stanzaType === 'message') {
this.receiveMessageStanza(stanza);
}
}
catch (err) {
this.logger.error('Error when processing a stanza: ' + err);
}
}
receivePresenceStanza(stanza) {
const from = stanza.from;
if (!from) {
this.logger.debug('[Room:receivePresenceStanza] no from, discard.');
return;
}
if (!from.getResource()) {
this.logger.debug('[Room:receivePresenceStanza] no resource in from, discard.');
return;
}
if (stanza.type === 'error') {
this.logger.error('[Room:receivePresenceStanza] Received error stanza. Dont deal with errors yet, discard');
return;
}
let user = this.roster.get(from.toString());
const newNickname = stanza.isNickNameChange();
if (newNickname !== false) {
if (!user) {
this.logger.error('[Room:receivePresenceStanza] Received a nickname change stanza, but the user is not known. Ignoring.');
return;
}
this.logger.debug('[Room:receivePresenceStanza] Detecting a nickname change, updating the roster');
const oldJID = new jid_1.JID(user.jid.local, user.jid.domain, user.jid.resource);
this.roster.delete(from.toString());
user.updateNick(newNickname);
this.roster.set(user.jid.toString(), user);
this.emit('room_nick_changed', user, oldJID);
return;
}
let updated = false;
if (!user) {
this.logger.debug('[Room:receivePresenceStanza] user was not in roster, creating it');
user = new user_1.RoomUser(this, stanza);
this.roster.set(from.toString(), user);
if (this.isOnline()) {
updated = true;
}
}
if (user.update(stanza)) {
updated = true;
}
if (user.isMe()) {
const previousState = this.state;
this.state = user.isOnline() ? 'online' : 'offline';
if (previousState !== 'online' && this.state === 'online') {
this.emit('room_roster_initialized', this.getOnlineUsers());
}
}
if (updated && this.isOnline()) {
if (user.isOnline()) {
this.logger.debug('[Room:receivePresenceStanza] user was previously not online, emitting room_joined event');
this.emit('room_joined', user);
}
else {
this.logger.debug('[Room:receivePresenceStanza] user was previously online, emitting room_parted event');
this.emit('room_parted', user);
}
}
}
receiveMessageStanza(stanza) {
const from = stanza.from;
if (!from) {
return;
}
if (!this.isOnline()) {
return;
}
if (stanza.type !== 'groupchat') {
return;
}
if (this.userJID && from.equals(this.userJID)) {
return;
}
if (stanza.isDelayed()) {
return;
}
const body = stanza.body();
if (!body) {
return;
}
const user = this.roster.get(from.toString());
if (!user) {
this.logger.error(`Cant find user ${from.toString()} in room ${this.roomJID.toString()} roster.`);
return;
}
this.emit('room_message', stanza, user);
if (body.startsWith('!')) {
const parameters = body.split(/\s+/);
const command = parameters.shift()?.substring(1);
if (command) {
this.emit('room_command', command, parameters, stanza, user);
}
}
if (this.userJID) {
const searchJIDs = [this.userJID];
const addr = this.bot.getAddress();
if (addr) {
searchJIDs.push(addr);
}
if (stanza.isMentionned(searchJIDs)) {
this.logger.debug('[HandlerRespond] Im mentionned in the message, using XMPP references');
this.emit('room_mentionned', stanza, user);
}
}
}
attachHandler(handler) {
if (handler.id in this.handlers) {
throw new Error('Can\'t attach handler, there is already an handler with the same id: ' + handler.id);
}
this.handlers[handler.id] = handler;
}
detachHandlers() {
this.logger.debug('Stoping and Detaching all handlers');
for (const id of Object.keys(this.handlers)) {
const handler = this.handlers[id];
handler.stop();
delete this.handlers[id];
}
}
getHandlerById(id) {
return this.handlers[id] ?? null;
}
detachHandlerById(id) {
const handler = this.handlers[id];
if (!handler) {
return;
}
this.logger.debug('Stoping and Detaching handler ' + id);
handler.stop();
delete this.handlers[id];
}
}
exports.Room = Room;
//# sourceMappingURL=room.js.map