Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7330aa7b1 | ||
|
|
ce71a7c7e5 | ||
|
|
a6a9531107 | ||
|
|
7ce65dc804 | ||
|
|
b2e7598554 | ||
|
|
b467e7d915 | ||
|
|
e21b174fe1 | ||
|
|
f2ea354717 | ||
|
|
fd27afb8f1 | ||
|
|
feac4c792a | ||
|
|
4d31ecd4eb | ||
|
|
fa83071b5c | ||
|
|
4f449954e0 | ||
|
|
d646ba149f | ||
|
|
6f89730ea8 | ||
|
|
e0f2edf2d4 | ||
|
|
3908e00d16 | ||
|
|
328fe0b44e | ||
|
|
5b01e7639f | ||
|
|
56d1c84147 | ||
|
|
5af02c16c6 | ||
|
|
f047ce0224 | ||
|
|
ee2d70fac1 | ||
|
|
91ad0b12d2 | ||
|
|
8a1378f864 | ||
|
|
8084ed42a7 | ||
|
|
6df5da619c | ||
|
|
7789bd6a24 | ||
|
|
1b7d3378aa | ||
|
|
8c97037930 | ||
|
|
8a02361758 |
@@ -4,7 +4,7 @@
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "6"
|
||||
"node": "12"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "airbnb-base",
|
||||
"parser": "babel-eslint",
|
||||
"parser": "@babel/eslint-parser",
|
||||
"env": {
|
||||
"mocha": true,
|
||||
"node": true
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# This workflow will build the project's Dockerfile and publish images to Docker Hub
|
||||
# For more information see: https://docs.github.com/en/actions/language-and-framework-guides/publishing-docker-images
|
||||
|
||||
name: Publish Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
release:
|
||||
types: [ published ]
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: Publish to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build and push to Docker Hub
|
||||
uses: docker/build-push-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
repository: discordirc/discord-irc
|
||||
tag_with_ref: true
|
||||
tag_with_sha: true
|
||||
@@ -0,0 +1,49 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 15.x, 16.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm run build --if-present
|
||||
- run: npm test
|
||||
- run: npm run save-coverage
|
||||
- name: Report to Coveralls (parallel)
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
flag-name: run-${{ matrix.node-version }}
|
||||
parallel: true
|
||||
|
||||
finish:
|
||||
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Report to Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
parallel-finished: true
|
||||
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
language: node_js
|
||||
after_success:
|
||||
- npm run report
|
||||
node_js:
|
||||
- '8'
|
||||
- '10'
|
||||
- '12'
|
||||
- '13'
|
||||
jobs:
|
||||
include:
|
||||
- stage: deploy
|
||||
if: branch = master AND type = push
|
||||
node_js: '12'
|
||||
services:
|
||||
- docker
|
||||
script: skip
|
||||
after_success: echo Skipping coverage.
|
||||
deploy:
|
||||
provider: script
|
||||
script: bash docker-deploy.sh
|
||||
on:
|
||||
branch: master
|
||||
+21
-3
@@ -1,6 +1,24 @@
|
||||
# Changelog
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [2.9.0] - 2020-10-03
|
||||
This release comes with a (relatively large) change to use discord.js v12!
|
||||
There were a lot of breaking changes in the upstream library, including removal of support for Node <12, which prompted a 2.9.0-alpha release before this main version.
|
||||
Everything seemed to go fine, which is why you're now receiving this latest update!
|
||||
|
||||
### Added
|
||||
* IRC nick colors are now customizable in the config (thanks to [KWeaver87](https://github.com/reactiflux/discord-irc/pull/561))
|
||||
|
||||
### Changed
|
||||
* Upgrade discord.js to 12.3.1 (the pre-release came with 12.2.0) - this changes a lot of how the bot works behind the scenes, and prepares us for upcoming Discord API changes - [#555](https://github.com/reactiflux/discord-irc/pull/555), [#564](https://github.com/reactiflux/discord-irc/pull/564)
|
||||
* Add support for Node 14 ([#549](https://github.com/reactiflux/discord-irc/pull/549)); drop support for Node 6, 8 and 10 ([#550](https://github.com/reactiflux/discord-irc/pull/550))
|
||||
|
||||
### Fixed
|
||||
* A bunch of vulnerabilities listed in `npm audit` are now gone - [#544](https://github.com/reactiflux/discord-irc/pull/544), [#548](https://github.com/reactiflux/discord-irc/pull/548), [#551](https://github.com/reactiflux/discord-irc/pull/551), [#552](https://github.com/reactiflux/discord-irc/pull/552), [#553](https://github.com/reactiflux/discord-irc/pull/553), [#562](https://github.com/reactiflux/discord-irc/pull/562)
|
||||
* Log messages now typically take up one line again (fixing a relatively long-standing bug, at this point – oops!) - [#554](https://github.com/reactiflux/discord-irc/pull/554)
|
||||
* Mentioning the same user more than once in a single message is now fixed (thanks to [Qyriad](https://github.com/reactiflux/discord-irc/pull/541))
|
||||
* Our Discord client now uses a retry limit of 3 (rather than 1) on internal server errors (hopefully fixing [#461](https://github.com/reactiflux/discord-irc/issues/461)) - [#565](https://github.com/reactiflux/discord-irc/pull/565)
|
||||
|
||||
## [2.8.1] - 2020-03-16
|
||||
### Fixed
|
||||
* Large avatars failed to display when bridging through webhooks - (thanks to [Miosame](https://github.com/reactiflux/discord-irc/pull/511), follow up in [#530](https://github.com/reactiflux/discord-irc/pull/530))
|
||||
@@ -12,7 +30,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
* `format.webhookAvatarURL`, to customize the unrecognized user webhook avatar (thanks to [Geo1088](https://github.com/reactiflux/discord-irc/pull/419))
|
||||
* `parallelPingFix`, disabled by default, to prevent users of both Discord and IRC getting pings whenever their messages are mirrored to IRC (thanks to [qaisjp](https://github.com/reactiflux/discord-irc/pull/243) originally, follow up in [#502](https://github.com/reactiflux/discord-irc/pull/502) and [#520](https://github.com/reactiflux/discord-irc/pull/520))
|
||||
* `ignoreUsers.discordIds`, to ignore specific Discord users through the bridge by ID instead of name (thanks to [nsavch](https://github.com/reactiflux/discord-irc/pull/508))
|
||||
* A basic Docker image, found at [discordirc/discord-irc](https://hub.docker.com/r/discordirc/discord-irc)! This may not be often updated with the release tags we publish on GitHub, but it should contain a `latest` tag in addition to whichever Git hash is available on master (thanks to [gdude2002](https://github.com/reactiflux/discord-irc/pull/421), follow up in [#498](https://github.com/reactiflux/discord-irc/pull/498))
|
||||
* A basic Docker image, found at [discordirc/discord-irc](https://hub.docker.com/r/discordirc/discord-irc)! This may not be often updated with the release tags we publish on GitHub, but it should contain a `latest` tag in addition to whichever Git hash is available on main (thanks to [gdude2002](https://github.com/reactiflux/discord-irc/pull/421), follow up in [#498](https://github.com/reactiflux/discord-irc/pull/498))
|
||||
|
||||
### Changed
|
||||
* Add support for Node 13, drop testing for Node 6 - [#521](https://github.com/reactiflux/discord-irc/pull/521)
|
||||
@@ -57,13 +75,13 @@ can appear as regular Discord messages:
|
||||

|
||||
|
||||
To enable webhooks, follow part 1 of [this
|
||||
guide](https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks)
|
||||
guide](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks)
|
||||
to create and retrieve a webhook URL for a specific channel, then enable it in
|
||||
discord-irc's config as follows:
|
||||
|
||||
```json
|
||||
"webhooks": {
|
||||
"#discord-channel": "https://discordapp.com/api/webhooks/id/token"
|
||||
"#discord-channel": "https://discord.com/api/webhooks/id/token"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
build:
|
||||
docker-compose -f docker-compose.yml up -d --build --remove-orphans
|
||||
@@ -1,12 +1,12 @@
|
||||
# discord-irc [](https://travis-ci.org/reactiflux/discord-irc) [](https://coveralls.io/github/reactiflux/discord-irc?branch=master)
|
||||
[](https://coveralls.io/github/reactiflux/discord-irc?branch=main)
|
||||
|
||||
> Connects [Discord](https://discordapp.com/) and [IRC](https://www.ietf.org/rfc/rfc1459.txt) channels by sending messages back and forth.
|
||||
> Connects [Discord](https://discord.com/) and [IRC](https://www.ietf.org/rfc/rfc1459.txt) channels by sending messages back and forth.
|
||||
|
||||
## Example
|
||||

|
||||
|
||||
## Installation and usage
|
||||
**Note**: discord-irc requires Node.js version 6 or newer, as it depends on [discord.js](https://github.com/hydrabolt/discord.js).
|
||||
**Note**: discord-irc requires Node.js version 12 or newer, as it depends on [discord.js](https://github.com/hydrabolt/discord.js).
|
||||
Future versions may require newer Node.js versions, though we should support active releases.
|
||||
|
||||
Before you can run discord-irc you need to create a configuration file by
|
||||
@@ -107,6 +107,7 @@ First you need to create a Discord bot user, which you can do by following the i
|
||||
"webhookAvatarURL": "https://robohash.org/{$nickname}" // Default avatar to use for webhook messages
|
||||
},
|
||||
"ircNickColor": false, // Gives usernames a color in IRC for better readability (on by default)
|
||||
"ircNickColors": ['light_blue', 'dark_blue', 'light_red', 'dark_red', 'light_green', 'dark_green', 'magenta', 'light_magenta', 'orange', 'yellow', 'cyan', 'light_cyan'], // Which irc-upd colors to use
|
||||
"parallelPingFix": true, // Prevents users of both IRC and Discord from being mentioned in IRC when they speak in Discord (off by default)
|
||||
// Makes the bot hide the username prefix for messages that start
|
||||
// with one of these characters (commands):
|
||||
@@ -119,7 +120,7 @@ First you need to create a Discord bot user, which you can do by following the i
|
||||
},
|
||||
// List of webhooks per channel
|
||||
"webhooks": {
|
||||
"#discord": "https://discordapp.com/api/webhooks/id/token"
|
||||
"#discord": "https://discord.com/api/webhooks/id/token"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -136,13 +137,13 @@ can appear as regular Discord messages:
|
||||

|
||||
|
||||
To enable webhooks, follow part 1 of [this
|
||||
guide](https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks)
|
||||
guide](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks)
|
||||
to create and retrieve a webhook URL for a specific channel, then enable it in
|
||||
discord-irc's config as follows:
|
||||
|
||||
```json
|
||||
"webhooks": {
|
||||
"#discord-channel": "https://discordapp.com/api/webhooks/id/token"
|
||||
"#discord-channel": "https://discord.com/api/webhooks/id/token"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
version: "3.9"
|
||||
services:
|
||||
discord-irc:
|
||||
image: discord-irc
|
||||
restart: unless-stopped
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./Dockerfile
|
||||
volumes:
|
||||
- ./config:/config/config.json
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "Connecting to docker hub"
|
||||
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||
|
||||
REPO=discordirc/discord-irc
|
||||
|
||||
echo "Building..."
|
||||
docker build -t $REPO:latest -t $REPO:$TRAVIS_COMMIT .
|
||||
|
||||
echo "Pushing image to Docker Hub..."
|
||||
docker push $REPO:latest
|
||||
docker push $REPO:$TRAVIS_COMMIT
|
||||
+37
-29
@@ -11,7 +11,7 @@ const USERNAME_MIN_LENGTH = 2;
|
||||
const USERNAME_MAX_LENGTH = 32;
|
||||
|
||||
const REQUIRED_FIELDS = ['server', 'nickname', 'channelMapping', 'discordToken'];
|
||||
const NICK_COLORS = ['light_blue', 'dark_blue', 'light_red', 'dark_red', 'light_green',
|
||||
const DEFAULT_NICK_COLORS = ['light_blue', 'dark_blue', 'light_red', 'dark_red', 'light_green',
|
||||
'dark_green', 'magenta', 'light_magenta', 'orange', 'yellow', 'cyan', 'light_cyan'];
|
||||
const patternMatch = /{\$(.+?)}/g;
|
||||
|
||||
@@ -29,7 +29,10 @@ class Bot {
|
||||
|
||||
validateChannelMapping(options.channelMapping);
|
||||
|
||||
this.discord = new discord.Client({ autoReconnect: true });
|
||||
this.discord = new discord.Client({
|
||||
autoReconnect: true,
|
||||
retryLimit: 3,
|
||||
});
|
||||
|
||||
this.server = options.server;
|
||||
this.nickname = options.nickname;
|
||||
@@ -37,6 +40,7 @@ class Bot {
|
||||
this.discordToken = options.discordToken;
|
||||
this.commandCharacters = options.commandCharacters || [];
|
||||
this.ircNickColor = options.ircNickColor !== false; // default to true
|
||||
this.ircNickColors = options.ircNickColors || DEFAULT_NICK_COLORS;
|
||||
this.parallelPingFix = options.parallelPingFix === true; // default: false
|
||||
this.channels = _.values(options.channelMapping);
|
||||
this.ircStatusNotices = options.ircStatusNotices;
|
||||
@@ -133,6 +137,12 @@ class Bot {
|
||||
this.attachListeners();
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.ircClient.disconnect();
|
||||
this.discord.destroy();
|
||||
Object.values(this.webhooks).forEach(x => x.client.destroy());
|
||||
}
|
||||
|
||||
attachListeners() {
|
||||
this.discord.on('ready', () => {
|
||||
logger.info('Connected to Discord');
|
||||
@@ -258,7 +268,7 @@ class Bot {
|
||||
|
||||
static getDiscordNicknameOnServer(user, guild) {
|
||||
if (guild) {
|
||||
const userDetails = guild.members.get(user.id);
|
||||
const userDetails = guild.members.cache.get(user.id);
|
||||
if (userDetails) {
|
||||
return userDetails.nickname || user.username;
|
||||
}
|
||||
@@ -269,20 +279,19 @@ class Bot {
|
||||
parseText(message) {
|
||||
const text = message.mentions.users.reduce((content, mention) => {
|
||||
const displayName = Bot.getDiscordNicknameOnServer(mention, message.guild);
|
||||
return content.replace(`<@${mention.id}>`, `@${displayName}`)
|
||||
.replace(`<@!${mention.id}>`, `@${displayName}`)
|
||||
.replace(`<@&${mention.id}>`, `@${displayName}`);
|
||||
const userMentionRegex = RegExp(`<@(&|!)?${mention.id}>`, 'g');
|
||||
return content.replace(userMentionRegex, `@${displayName}`);
|
||||
}, message.content);
|
||||
|
||||
return text
|
||||
.replace(/\n|\r\n|\r/g, ' ')
|
||||
.replace(/<#(\d+)>/g, (match, channelId) => {
|
||||
const channel = this.discord.channels.get(channelId);
|
||||
const channel = this.discord.channels.cache.get(channelId);
|
||||
if (channel) return `#${channel.name}`;
|
||||
return '#deleted-channel';
|
||||
})
|
||||
.replace(/<@&(\d+)>/g, (match, roleId) => {
|
||||
const role = message.guild.roles.get(roleId);
|
||||
const role = message.guild.roles.cache.get(roleId);
|
||||
if (role) return `@${role.name}`;
|
||||
return '@deleted-role';
|
||||
})
|
||||
@@ -339,8 +348,8 @@ class Bot {
|
||||
}
|
||||
|
||||
if (this.ircNickColor) {
|
||||
const colorIndex = (nickname.charCodeAt(0) + nickname.length) % NICK_COLORS.length;
|
||||
displayUsername = irc.colors.wrap(NICK_COLORS[colorIndex], displayUsername);
|
||||
const colorIndex = (nickname.charCodeAt(0) + nickname.length) % this.ircNickColors.length;
|
||||
displayUsername = irc.colors.wrap(this.ircNickColors[colorIndex], displayUsername);
|
||||
}
|
||||
|
||||
const patternMap = {
|
||||
@@ -391,10 +400,10 @@ class Bot {
|
||||
// #channel -> channel before retrieving and select only text channels:
|
||||
let discordChannel = null;
|
||||
|
||||
if (this.discord.channels.has(discordChannelName)) {
|
||||
discordChannel = this.discord.channels.get(discordChannelName);
|
||||
if (this.discord.channels.cache.has(discordChannelName)) {
|
||||
discordChannel = this.discord.channels.cache.get(discordChannelName);
|
||||
} else if (discordChannelName.startsWith('#')) {
|
||||
discordChannel = this.discord.channels
|
||||
discordChannel = this.discord.channels.cache
|
||||
.filter(c => c.type === 'text')
|
||||
.find(c => c.name === discordChannelName.slice(1));
|
||||
}
|
||||
@@ -417,7 +426,7 @@ class Bot {
|
||||
}
|
||||
|
||||
getDiscordAvatar(nick, channel) {
|
||||
const guildMembers = this.findDiscordChannel(channel).guild.members;
|
||||
const guildMembers = this.findDiscordChannel(channel).guild.members.cache;
|
||||
const findByNicknameOrUsername = caseSensitive =>
|
||||
(member) => {
|
||||
if (caseSensitive) {
|
||||
@@ -438,8 +447,8 @@ class Bot {
|
||||
|
||||
// No matching user or more than one => default avatar
|
||||
if (users && users.size === 1) {
|
||||
const url = users.first().user.avatarURL;
|
||||
if (url) return url.replace(/\?size=\d{1,}$/, '?size=128');
|
||||
const url = users.first().user.avatarURL({ size: 128, format: 'png' });
|
||||
if (url) return url;
|
||||
}
|
||||
|
||||
// If there isn't a URL format, don't send an avatar at all
|
||||
@@ -498,7 +507,7 @@ class Bot {
|
||||
// @username#1234 => mention
|
||||
// skips usernames including spaces for ease (they cannot include hashes)
|
||||
// checks case insensitively as Discord does
|
||||
const user = guild.members.find(x =>
|
||||
const user = guild.members.cache.find(x =>
|
||||
Bot.caseComp(x.user.username, username)
|
||||
&& x.user.discriminator === discriminator);
|
||||
if (user) return user;
|
||||
@@ -510,16 +519,16 @@ class Bot {
|
||||
// this preliminary stuff is ultimately unnecessary
|
||||
// but might save time over later more complicated calculations
|
||||
// @nickname => mention, case insensitively
|
||||
const nickUser = guild.members.find(x =>
|
||||
x.nickname !== null && Bot.caseComp(x.nickname, reference));
|
||||
const nickUser = guild.members.cache.find(x =>
|
||||
x.nickname && Bot.caseComp(x.nickname, reference));
|
||||
if (nickUser) return nickUser;
|
||||
|
||||
// @username => mention, case insensitively
|
||||
const user = guild.members.find(x => Bot.caseComp(x.user.username, reference));
|
||||
const user = guild.members.cache.find(x => Bot.caseComp(x.user.username, reference));
|
||||
if (user) return user;
|
||||
|
||||
// @role => mention, case insensitively
|
||||
const role = guild.roles.find(x => x.mentionable && Bot.caseComp(x.name, reference));
|
||||
const role = guild.roles.cache.find(x => x.mentionable && Bot.caseComp(x.name, reference));
|
||||
if (role) return role;
|
||||
|
||||
// No match found checking the whole word. Check for partial matches now instead.
|
||||
@@ -544,13 +553,13 @@ class Bot {
|
||||
};
|
||||
|
||||
// check users by username and nickname
|
||||
guild.members.forEach((member) => {
|
||||
guild.members.cache.forEach((member) => {
|
||||
checkMatch(member.user.username, member);
|
||||
if (bestMatch === member || member.nickname === null) return;
|
||||
if (bestMatch === member || !member.nickname) return;
|
||||
checkMatch(member.nickname, member);
|
||||
});
|
||||
// check mentionable roles by visible name
|
||||
guild.roles.forEach((member) => {
|
||||
guild.roles.cache.forEach((member) => {
|
||||
if (!member.mentionable) return;
|
||||
checkMatch(member.name, member);
|
||||
});
|
||||
@@ -561,7 +570,7 @@ class Bot {
|
||||
return match;
|
||||
}).replace(/:(\w+):/g, (match, ident) => {
|
||||
// :emoji: => mention, case sensitively
|
||||
const emoji = guild.emojis.find(x => x.name === ident && x.requiresColons);
|
||||
const emoji = guild.emojis.cache.find(x => x.name === ident && x.requiresColons);
|
||||
if (emoji) return emoji;
|
||||
|
||||
return match;
|
||||
@@ -571,7 +580,7 @@ class Bot {
|
||||
// but these seem likely to be common around channel references)
|
||||
|
||||
// discord matches channel names case insensitively
|
||||
const chan = guild.channels.find(x => Bot.caseComp(x.name, channelName));
|
||||
const chan = guild.channels.cache.find(x => Bot.caseComp(x.name, channelName));
|
||||
return chan || match;
|
||||
});
|
||||
|
||||
@@ -586,11 +595,10 @@ class Bot {
|
||||
}
|
||||
const avatarURL = this.getDiscordAvatar(author, channel);
|
||||
const username = _.padEnd(author.substring(0, USERNAME_MAX_LENGTH), USERNAME_MIN_LENGTH, '_');
|
||||
webhook.client.sendMessage(withMentions, {
|
||||
webhook.client.send(withMentions, {
|
||||
username,
|
||||
text,
|
||||
avatarURL,
|
||||
disableEveryone: !canPingEveryone,
|
||||
disableMentions: canPingEveryone ? 'none' : 'everyone',
|
||||
}).catch(logger.error);
|
||||
return;
|
||||
}
|
||||
|
||||
+4
-3
@@ -3,7 +3,6 @@
|
||||
import fs from 'fs';
|
||||
import program from 'commander';
|
||||
import path from 'path';
|
||||
import checkEnv from 'check-env';
|
||||
import stripJsonComments from 'strip-json-comments';
|
||||
import { endsWith } from 'lodash';
|
||||
import * as helpers from './helpers';
|
||||
@@ -32,9 +31,11 @@ function run() {
|
||||
)
|
||||
.parse(process.argv);
|
||||
|
||||
const opts = program.opts();
|
||||
|
||||
// If no config option is given, try to use the env variable:
|
||||
if (!program.config) checkEnv(['CONFIG_FILE']);
|
||||
else process.env.CONFIG_FILE = program.config;
|
||||
if (opts.config) process.env.CONFIG_FILE = opts.config;
|
||||
if (!process.env.CONFIG_FILE) throw new Error('Missing environment variable CONFIG_FILE');
|
||||
|
||||
const completePath = path.resolve(process.cwd(), process.env.CONFIG_FILE);
|
||||
const config = endsWith(process.env.CONFIG_FILE, '.js') ?
|
||||
|
||||
+9
-15
@@ -1,23 +1,17 @@
|
||||
import winston, { format } from 'winston';
|
||||
import { inspect } from 'util';
|
||||
|
||||
function simpleInspect(value) {
|
||||
if (typeof value === 'string') return value;
|
||||
return inspect(value, { depth: null });
|
||||
}
|
||||
|
||||
function formatter(info) {
|
||||
const stringifiedRest = inspect(
|
||||
{
|
||||
...info,
|
||||
level: undefined,
|
||||
message: undefined,
|
||||
splat: undefined
|
||||
},
|
||||
{ depth: null }
|
||||
);
|
||||
const splat = info[Symbol.for('splat')] || [];
|
||||
const stringifiedRest = splat.length > 0 ? ` ${splat.map(simpleInspect).join(' ')}` : '';
|
||||
|
||||
const padding = (info.padding && info.padding[info.level]) || '';
|
||||
if (stringifiedRest !== '{}') {
|
||||
return `${info.timestamp} ${info.level}:${padding} ${info.message} ${stringifiedRest}`;
|
||||
}
|
||||
|
||||
return `${info.timestamp} ${info.level}:${padding} ${info.message}`;
|
||||
return `${info.timestamp} ${info.level}:${padding} ${info.message}${stringifiedRest}`;
|
||||
}
|
||||
|
||||
const logger = winston.createLogger({
|
||||
@@ -26,7 +20,7 @@ const logger = winston.createLogger({
|
||||
format: format.combine(
|
||||
format.colorize(),
|
||||
format.timestamp(),
|
||||
format.printf(formatter)
|
||||
format.printf(formatter),
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
Generated
+3311
-5507
File diff suppressed because it is too large
Load Diff
+27
-28
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "discord-irc",
|
||||
"version": "2.8.1",
|
||||
"version": "2.9.0",
|
||||
"description": "Connects IRC and Discord channels by sending messages back and forth.",
|
||||
"keywords": [
|
||||
"discord",
|
||||
@@ -11,7 +11,7 @@
|
||||
"reactiflux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"bin": "dist/index.js",
|
||||
@@ -28,7 +28,7 @@
|
||||
"prepare": "npm run build",
|
||||
"lint": "eslint . --ignore-path .gitignore",
|
||||
"coverage": "cross-env NODE_ENV=test nyc --require @babel/register mocha -- 'test/*.test.js'",
|
||||
"report": "nyc report --reporter=text-lcov | coveralls",
|
||||
"save-coverage": "nyc report --reporter=lcov",
|
||||
"test": "npm run lint && npm run coverage"
|
||||
},
|
||||
"author": {
|
||||
@@ -36,34 +36,33 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"check-env": "1.3.0",
|
||||
"commander": "^4.0.1",
|
||||
"discord.js": "11.5.1",
|
||||
"commander": "^7.2.0",
|
||||
"discord.js": "^12.5.3",
|
||||
"irc-colors": "1.5.0",
|
||||
"irc-formatting": "1.0.0-rc3",
|
||||
"irc-upd": "0.10.0",
|
||||
"lodash": "^4.17.15",
|
||||
"simple-markdown": "^0.7.1",
|
||||
"strip-json-comments": "3.0.1",
|
||||
"winston": "3.2.1"
|
||||
"irc-upd": "0.11.0",
|
||||
"lodash": "^4.17.21",
|
||||
"simple-markdown": "^0.7.3",
|
||||
"strip-json-comments": "^3.1.1",
|
||||
"winston": "^3.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.7.6",
|
||||
"@babel/register": "^7.7.4",
|
||||
"@istanbuljs/nyc-config-babel": "^2.1.1",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-plugin-istanbul": "^5.2.0",
|
||||
"chai": "^4.2.0",
|
||||
"coveralls": "^3.0.9",
|
||||
"cross-env": "^6.0.3",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-config-airbnb-base": "^14.0.0",
|
||||
"eslint-plugin-import": "^2.19.1",
|
||||
"mocha": "^6.2.2",
|
||||
"nyc": "^14.1.1",
|
||||
"sinon": "^7.5.0",
|
||||
"sinon-chai": "^3.3.0"
|
||||
"@babel/cli": "^7.13.16",
|
||||
"@babel/core": "^7.13.16",
|
||||
"@babel/eslint-parser": "^7.13.14",
|
||||
"@babel/preset-env": "^7.13.15",
|
||||
"@babel/register": "^7.13.16",
|
||||
"@istanbuljs/nyc-config-babel": "^3.0.0",
|
||||
"babel-plugin-istanbul": "^6.0.0",
|
||||
"chai": "^4.3.4",
|
||||
"coveralls": "^3.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^7.25.0",
|
||||
"eslint-config-airbnb-base": "^14.2.1",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"mocha": "^8.3.2",
|
||||
"nyc": "^15.1.0",
|
||||
"sinon": "^10.0.0",
|
||||
"sinon-chai": "^3.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import discord from 'discord.js';
|
||||
import Bot from '../lib/bot';
|
||||
import logger from '../lib/logger';
|
||||
import createDiscordStub from './stubs/discord-stub';
|
||||
import createWebhookStub from './stubs/webhook-stub';
|
||||
import ClientStub from './stubs/irc-client-stub';
|
||||
import config from './fixtures/single-test-config.json';
|
||||
|
||||
@@ -34,9 +35,9 @@ describe('Bot Events', function () {
|
||||
this.warnSpy = sandbox.stub(logger, 'warn');
|
||||
this.errorSpy = sandbox.stub(logger, 'error');
|
||||
this.sendStub = sandbox.stub();
|
||||
this.getUserStub = sandbox.stub();
|
||||
irc.Client = ClientStub;
|
||||
discord.Client = createDiscordStub(this.sendStub, this.getUserStub);
|
||||
discord.Client = createDiscordStub(this.sendStub);
|
||||
discord.WebhookClient = createWebhookStub(this.sendStub);
|
||||
ClientStub.prototype.send = sandbox.stub();
|
||||
ClientStub.prototype.join = sandbox.stub();
|
||||
this.bot = createBot();
|
||||
@@ -44,6 +45,7 @@ describe('Bot Events', function () {
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.bot.disconnect();
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
|
||||
+40
-23
@@ -28,9 +28,8 @@ describe('Bot', function () {
|
||||
this.errorSpy = sandbox.stub(logger, 'error');
|
||||
this.sendStub = sandbox.stub();
|
||||
|
||||
this.discordUsers = new discord.Collection();
|
||||
irc.Client = ClientStub;
|
||||
discord.Client = createDiscordStub(this.sendStub, this.discordUsers);
|
||||
discord.Client = createDiscordStub(this.sendStub);
|
||||
|
||||
ClientStub.prototype.say = sandbox.stub();
|
||||
ClientStub.prototype.send = sandbox.stub();
|
||||
@@ -40,7 +39,7 @@ describe('Bot', function () {
|
||||
|
||||
this.setCustomBot = conf => {
|
||||
this.bot = new Bot(conf);
|
||||
this.guild = this.bot.discord.guild;
|
||||
this.guild = this.bot.discord.guilds.cache.first();
|
||||
this.bot.connect();
|
||||
};
|
||||
|
||||
@@ -53,21 +52,21 @@ describe('Bot', function () {
|
||||
// also set guild members
|
||||
const guildMember = { ...(member || user), user: userObj };
|
||||
guildMember.nick = guildMember.nickname; // nick => nickname in Discord API
|
||||
const memberObj = new discord.GuildMember(this.guild, guildMember);
|
||||
this.guild.members.set(userObj.id, memberObj);
|
||||
this.discordUsers.set(userObj.id, userObj);
|
||||
const memberObj = new discord.GuildMember(this.bot.discord, guildMember, this.guild);
|
||||
this.guild.members.cache.set(userObj.id, memberObj);
|
||||
this.bot.discord.users.cache.set(userObj.id, userObj);
|
||||
return memberObj;
|
||||
};
|
||||
|
||||
this.addRole = function (role) {
|
||||
const roleObj = new discord.Role(this.guild, role);
|
||||
this.guild.roles.set(roleObj.id, roleObj);
|
||||
const roleObj = new discord.Role(this.bot.discord, role, this.guild);
|
||||
this.guild.roles.cache.set(roleObj.id, roleObj);
|
||||
return roleObj;
|
||||
};
|
||||
|
||||
this.addEmoji = function (emoji) {
|
||||
const emojiObj = new discord.Emoji(this.guild, emoji);
|
||||
this.guild.emojis.set(emojiObj.id, emojiObj);
|
||||
const emojiObj = new discord.GuildEmoji(this.bot.discord, emoji, this.guild);
|
||||
this.guild.emojis.cache.set(emojiObj.id, emojiObj);
|
||||
return emojiObj;
|
||||
};
|
||||
});
|
||||
@@ -173,6 +172,28 @@ describe('Bot', function () {
|
||||
ClientStub.prototype.say.should.have.been.calledWith('#irc', expected);
|
||||
});
|
||||
|
||||
it('should only use message color defined in config', function () {
|
||||
const text = 'testmessage';
|
||||
const newConfig = { ...config, ircNickColors: ['orange'] };
|
||||
this.setCustomBot(newConfig);
|
||||
const message = {
|
||||
content: text,
|
||||
mentions: { users: [] },
|
||||
channel: {
|
||||
name: 'discord'
|
||||
},
|
||||
author: {
|
||||
username: 'otherauthor',
|
||||
id: 'not bot id'
|
||||
},
|
||||
guild: this.guild
|
||||
};
|
||||
|
||||
this.bot.sendToIRC(message);
|
||||
const expected = `<\u000307${message.author.username}\u000f> ${text}`;
|
||||
ClientStub.prototype.say.should.have.been.calledWith('#irc', expected);
|
||||
});
|
||||
|
||||
it('should send correct messages to irc', function () {
|
||||
const text = 'testmessage';
|
||||
const message = {
|
||||
@@ -627,7 +648,7 @@ describe('Bot', function () {
|
||||
this.setCustomBot(newConfig);
|
||||
const id = 'not bot id';
|
||||
const nickname = 'discord-nickname';
|
||||
this.guild.members.set(id, { nickname });
|
||||
this.guild.members.cache.set(id, { nickname });
|
||||
const message = {
|
||||
content: text,
|
||||
mentions: { users: [] },
|
||||
@@ -988,7 +1009,7 @@ describe('Bot', function () {
|
||||
|
||||
context('with enabled Discord webhook', function () {
|
||||
this.beforeEach(function () {
|
||||
const newConfig = { ...config, webhooks: { '#discord': 'https://discordapp.com/api/webhooks/id/token' } };
|
||||
const newConfig = { ...config, webhooks: { '#discord': 'https://discord.com/api/webhooks/id/token' } };
|
||||
this.setCustomBot(newConfig);
|
||||
});
|
||||
|
||||
@@ -1002,9 +1023,8 @@ describe('Bot', function () {
|
||||
this.bot.sendToDiscord('n', '#irc', text);
|
||||
this.sendWebhookMessageStub.should.have.been.calledWith(text, {
|
||||
username: 'n_',
|
||||
text,
|
||||
avatarURL: null,
|
||||
disableEveryone: true,
|
||||
disableMentions: 'everyone',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1013,9 +1033,8 @@ describe('Bot', function () {
|
||||
this.bot.sendToDiscord('1234567890123456789012345678901234567890', '#irc', text);
|
||||
this.sendWebhookMessageStub.should.have.been.calledWith(text, {
|
||||
username: '12345678901234567890123456789012',
|
||||
text,
|
||||
avatarURL: null,
|
||||
disableEveryone: true,
|
||||
disableMentions: 'everyone',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1023,16 +1042,15 @@ describe('Bot', function () {
|
||||
const text = 'message';
|
||||
const permission = discord.Permissions.FLAGS.VIEW_CHANNEL
|
||||
+ discord.Permissions.FLAGS.SEND_MESSAGES;
|
||||
this.bot.discord.channels.get('1234').setPermissionStub(
|
||||
this.bot.discord.channels.cache.get('1234').setPermissionStub(
|
||||
this.bot.discord.user,
|
||||
new discord.Permissions(permission),
|
||||
);
|
||||
this.bot.sendToDiscord('nick', '#irc', text);
|
||||
this.sendWebhookMessageStub.should.have.been.calledWith(text, {
|
||||
username: 'nick',
|
||||
text,
|
||||
avatarURL: null,
|
||||
disableEveryone: true,
|
||||
disableMentions: 'everyone',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1041,16 +1059,15 @@ describe('Bot', function () {
|
||||
const permission = discord.Permissions.FLAGS.VIEW_CHANNEL
|
||||
+ discord.Permissions.FLAGS.SEND_MESSAGES
|
||||
+ discord.Permissions.FLAGS.MENTION_EVERYONE;
|
||||
this.bot.discord.channels.get('1234').setPermissionStub(
|
||||
this.bot.discord.channels.cache.get('1234').setPermissionStub(
|
||||
this.bot.discord.user,
|
||||
new discord.Permissions(permission),
|
||||
);
|
||||
this.bot.sendToDiscord('nick', '#irc', text);
|
||||
this.sendWebhookMessageStub.should.have.been.calledWith(text, {
|
||||
username: 'nick',
|
||||
text,
|
||||
avatarURL: null,
|
||||
disableEveryone: false,
|
||||
disableMentions: 'none',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1118,7 +1135,7 @@ describe('Bot', function () {
|
||||
|
||||
context('when matching avatars with fallback URL', function () {
|
||||
this.beforeEach(function () {
|
||||
const newConfig = { ...config, webhooks: { '#discord': 'https://discordapp.com/api/webhooks/id/token' }, format: { webhookAvatarURL: 'avatarFrom/{$nickname}' } };
|
||||
const newConfig = { ...config, webhooks: { '#discord': 'https://discord.com/api/webhooks/id/token' }, format: { webhookAvatarURL: 'avatarFrom/{$nickname}' } };
|
||||
this.setCustomBot(newConfig);
|
||||
|
||||
setupUser(this);
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
"#withwebhook": "#ircwebhook"
|
||||
},
|
||||
"webhooks": {
|
||||
"#withwebhook": "https://discordapp.com/api/webhooks/id/token"
|
||||
"#withwebhook": "https://discord.com/api/webhooks/id/token"
|
||||
},
|
||||
"ignoreUsers": {
|
||||
"irc": ["irc_ignored_user"],
|
||||
|
||||
+24
-14
@@ -3,29 +3,33 @@ import events from 'events';
|
||||
import sinon from 'sinon';
|
||||
import discord from 'discord.js';
|
||||
|
||||
export default function createDiscordStub(sendStub, discordUsers) {
|
||||
export default function createDiscordStub(sendStub) {
|
||||
return class DiscordStub extends events.EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
this.user = {
|
||||
id: 'testid'
|
||||
};
|
||||
this.channels = new discord.Collection();
|
||||
this.channels = new discord.ChannelManager(this, []);
|
||||
this.options = {
|
||||
http: {
|
||||
cdn: ''
|
||||
}
|
||||
};
|
||||
|
||||
this.users = discordUsers;
|
||||
this.guilds = new discord.Collection();
|
||||
this.guild = this.createGuildStub();
|
||||
this.guilds.set(this.guild.id, this.guild);
|
||||
this.users = new discord.UserManager(this, []);
|
||||
this.guilds = new discord.GuildManager(this, []);
|
||||
const guild = this.createGuildStub();
|
||||
this.guilds.cache.set(guild.id, guild);
|
||||
|
||||
this.rest = this.createRestStub();
|
||||
}
|
||||
|
||||
destroy() {}
|
||||
|
||||
addTextChannel(guild, textChannel) {
|
||||
const textChannelData = {
|
||||
type: 'text',
|
||||
type: discord.Constants.ChannelTypes.TEXT,
|
||||
...textChannel
|
||||
};
|
||||
const textChannelObj = new discord.TextChannel(guild, textChannelData);
|
||||
@@ -33,7 +37,7 @@ export default function createDiscordStub(sendStub, discordUsers) {
|
||||
const permissions = new discord.Collection();
|
||||
textChannelObj.setPermissionStub = (user, perms) => permissions.set(user, perms);
|
||||
textChannelObj.permissionsFor = user => permissions.get(user);
|
||||
this.channels.set(textChannelObj.id, textChannelObj);
|
||||
this.channels.cache.set(textChannelObj.id, textChannelObj);
|
||||
return textChannelObj;
|
||||
}
|
||||
|
||||
@@ -41,18 +45,18 @@ export default function createDiscordStub(sendStub, discordUsers) {
|
||||
const guild = {
|
||||
id: '1',
|
||||
client: this,
|
||||
roles: new discord.Collection(),
|
||||
members: new discord.Collection(),
|
||||
emojis: new discord.Collection(),
|
||||
channels: new discord.Collection(),
|
||||
addTextChannel: (textChannel) => {
|
||||
const textChannelObj = this.addTextChannel(guild, textChannel);
|
||||
textChannelObj.guild.channels.set(textChannelObj.id, textChannelObj);
|
||||
textChannelObj.guild.channels.cache.set(textChannelObj.id, textChannelObj);
|
||||
return textChannelObj;
|
||||
}
|
||||
};
|
||||
guild.roles = new discord.RoleManager(guild, []);
|
||||
guild.members = new discord.GuildMemberManager(guild, []);
|
||||
guild.emojis = new discord.GuildEmojiManager(guild, []);
|
||||
guild.channels = new discord.GuildChannelManager(guild, []);
|
||||
Object.assign(guild, guildData);
|
||||
this.guilds.set(guild.id, guild);
|
||||
this.guilds.cache.set(guild.id, guild);
|
||||
|
||||
if (guild.id === '1') {
|
||||
guild.addTextChannel({
|
||||
@@ -64,6 +68,12 @@ export default function createDiscordStub(sendStub, discordUsers) {
|
||||
return guild;
|
||||
}
|
||||
|
||||
createRestStub() {
|
||||
return {
|
||||
cdn: discord.Constants.Endpoints.CDN(''),
|
||||
};
|
||||
}
|
||||
|
||||
login() {
|
||||
return sinon.stub();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import events from 'events';
|
||||
|
||||
/* eslint-disable class-methods-use-this */
|
||||
class ClientStub extends events.EventEmitter {
|
||||
constructor(...args) {
|
||||
super();
|
||||
this.nick = args[1]; // eslint-disable-line prefer-destructuring
|
||||
}
|
||||
|
||||
disconnect() {}
|
||||
}
|
||||
|
||||
export default ClientStub;
|
||||
|
||||
@@ -6,9 +6,11 @@ export default function createWebhookStub(sendWebhookMessage) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
sendMessage(...args) {
|
||||
send(...args) {
|
||||
sendWebhookMessage(...args);
|
||||
return new Promise(() => {});
|
||||
}
|
||||
|
||||
destroy() {}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user