Compare commits

...
106 Commits
Author SHA1 Message Date
marc e7330aa7b1 no push statement 2021-12-25 19:11:37 +01:00
marc ce71a7c7e5 docker-compose 2021-12-25 19:10:10 +01:00
marc a6a9531107 build before push 2021-12-25 19:01:11 +01:00
marc 7ce65dc804 docker-compose and makefile 2021-12-25 18:58:44 +01:00
dependabot[bot] b2e7598554 Bump ws from 7.4.5 to 7.4.6 (#593)
Publish Docker image / Publish to Docker Hub (push) Canceled after 0s
Node.js CI / test (12.x) (push) Canceled after 0s
Node.js CI / test (14.x) (push) Canceled after 0s
Node.js CI / test (15.x) (push) Canceled after 0s
Node.js CI / test (16.x) (push) Canceled after 0s
Node.js CI / finish (push) Canceled after 0s
Bumps [ws](https://github.com/websockets/ws) from 7.4.5 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.5...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-23 23:01:41 -03:00
Edward Jones b467e7d915 Upgrade deps as of 2021-04-24 (#590)
* Bump discord.js to 12.5.3

* Bump commander to ^7.2.0

* Bump simple-markdown to ^0.7.3

* Bump @babel dependencies to ^7.13

* Bump cross-env to ^7.0.3

(Nothing relevant; just went to maintenance-only mode)

* Bump lodash to ^4.17.21

* Bump eslint dependencies

* Bump test dependencies (chai, mocha, sinon, sinon-chai)

* Replace babel-eslint with @babel/eslint-parser

* Bump sub-dependencies
2021-04-24 02:23:24 -03:00
Edward Jones e21b174fe1 Add support for Node 15, 16 (#589) 2021-04-24 00:36:26 -03:00
Edward Jones f2ea354717 Drop support for Node 13 (#588)
EOL since June 2020.
2021-04-24 00:31:03 -03:00
dependabot[bot] fd27afb8f1 Bump y18n from 4.0.0 to 4.0.3 (#587)
Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.3.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/y18n-v4.0.3/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/compare/v4.0.0...y18n-v4.0.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-24 00:26:56 -03:00
Edward Jones feac4c792a Use node-version for coveralls flag-name key (#586)
Should fix coveralls reporting, hopefully
2021-04-24 00:24:04 -03:00
Nick McCurdy 4d31ecd4eb Remove Travis badge
We're using GitHub Actions now.
2021-01-22 19:04:52 -05:00
Nick McCurdy fa83071b5c Change default branch to main 2021-01-22 18:59:59 -05:00
Nick McCurdy 4f449954e0 Replace discordapp.com with discord.com (#567) 2020-11-09 20:33:03 -03:00
Edward Jones d646ba149f 2.9.0 2020-10-03 16:47:42 -03:00
Edward Jones 6f89730ea8 Update changelog for 2.9.0 2020-10-03 16:47:28 -03:00
Edward Jones e0f2edf2d4 Add retryLimit of 3 to the Discord client (#565)
Intended to avoid issues with 500 errors returned
from Discord, as in #461.
2020-10-03 16:35:16 -03:00
Edward Jones 3908e00d16 Upgrade discord.js to v12.3.1 (#564) 2020-10-03 16:30:42 -03:00
dependabot[bot] 328fe0b44e Bump node-fetch from 2.6.0 to 2.6.1 (#562)
Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/bitinn/node-fetch/releases)
- [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-09-13 11:43:58 -03:00
KWeaver87andTJ22 5b01e7639f Add customizable IRC nick colors in config (#561)
Co-authored-by: TJ22 <tacojet42@yahoo.com>
2020-09-05 12:41:00 -03:00
Mikaela Szekely 56d1c84147 Fix parsing when the same user is mentioned more than once (#541)
Before, the parser would only replace the first instance of a mention
for any given user in a message.
2020-08-14 21:35:48 -03:00
Edward Jones 5af02c16c6 Add 2.9.0-alpha to CHANGELOG 2020-08-09 02:17:32 -03:00
Edward Jones f047ce0224 Upgrade discord.js to 12.2.0 (#555)
* Upgrade discord.js to 12.2.0

* Use cache accessors in Discord client

* Replace sendMessage with send

* Use avatarURL method

* Update test stubs for discord.js 12

* Disconnect clients at end of tests

* Fix disabling webhook mentions when bot lacks permissions
2020-08-09 02:07:09 -03:00
Edward Jones ee2d70fac1 Fix winston logging to be primarily single-line (#554) 2020-08-08 23:32:21 -03:00
Edward Jones 91ad0b12d2 Update regular dependencies for 2020-08-08 (#553)
* Upgrade irc-upd to 0.11.0

* Bump lodash to ^4.17.19 in package.json

* Upgrade simple-markdown to ^0.7.2

* Upgrade strip-json-comments to ^3.1.1

* Upgrade winston to ^3.3.3

* Upgrade commander to ^5.1.0

* Upgrade commander to ^6.0.0
2020-08-08 22:18:36 -03:00
Edward Jones 8a1378f864 Inline check-env dependency (#552) 2020-08-08 21:53:04 -03:00
Edward Jones 8084ed42a7 Update devDependencies for 2020-08-08 (#551)
* Update eslint dependencies for eslint ^7.6.0

* Upgrade nyc dependencies for nyc ^15.1.0

* Upgrade sinon-chai to ^3.5.0

* Upgrade sinon to ^9.0.2

* Upgrade cross-env to ^7.0.2

* Upgrade coveralls to ^3.1.0

* Upgrade mocha to ^7.2.0

* Upgrade mocha to ^8.1.1
2020-08-08 21:47:10 -03:00
Edward Jones 6df5da619c Drop support for Node <12 (#550)
Upcoming updates to discord require us to update our
version of discord.js, which in turn requires Node >=12.
2020-08-08 21:18:52 -03:00
Edward Jones 7789bd6a24 Add testing for Node 14 (#549) 2020-08-08 21:12:42 -03:00
Edward Jones 1b7d3378aa Update babel devDependencies to recent versions (minor) (#548) 2020-08-08 21:09:24 -03:00
dependabot[bot] 8c97037930 Bump lodash from 4.17.15 to 4.17.19 (#544)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-08-08 20:39:19 -03:00
Edward Jones 8a02361758 Use NodeJS Action instead of TravisCI (#547)
* Create NodeJS Action

Looks like Travis is a bit broken, and I don't have admin access to the repo over there. For now, as I have admin access to this repo here, let's try out GitHub Actions to replace the old CI.

* Create save-coverage script for Coveralls

* Delete .travis.yml

* Add Docker Hub workflow

* Remove docker-deploy.sh
2020-08-08 20:35:58 -03:00
Edward Jones 41f8444d17 2.8.1 2020-03-16 22:10:55 -03:00
Edward Jones eaf8b3d470 Update changelog for 2.8.1 2020-03-16 22:10:50 -03:00
Edward Jones b3ebca5dcb Use istanbul babel plugin only in tests (#536)
Prevents coverage instrumentation code from being
in the final package.

Fixes #535.
2020-03-16 21:59:10 -03:00
dependabot[bot] 9585a70eb8 Bump acorn from 7.1.0 to 7.1.1 (#534)
Bumps [acorn](https://github.com/acornjs/acorn) from 7.1.0 to 7.1.1.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/7.1.0...7.1.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-03-16 21:53:04 -03:00
Edward Jones 4fe14837db Fix webhooks for users with no avatars (#530)
* Consistently use this.bot in tests

Some tests were subtly testing broken behavior,
as this.addUser was not correctly adding a user
to the given bot's guild.

* Group up webhook tests in contexts

* Fix webhooks for users with no avatars

Fixes #529.
2020-01-04 18:24:00 -03:00
Miosame 88fecf3aa1 Fix avatars not displaying via webhooks (#511)
* Fix avatars not displaying via webhooks

Avatars that were 2048x2048 would break webhooks displaying avatars, defaulting to 128x128 fixes that.

* travis-ci fix

* Fix null-checks

* Fix travis-ci tests for avatar size
2019-12-24 15:12:28 +01:00
Edward Jones 03d6ac2170 2.8.0 2019-12-14 17:42:52 -03:00
Edward Jones c5ad1a6a6a Upgrade eslint & commander (out of current ranges) (#525)
* Upgrade eslint-config-airbnb-base to ^14.0.0

* Upgrade eslint to ^6.7.2

* Upgrade commander to ^4.0.1
2019-12-14 17:21:01 -03:00
Edward Jones 33d12d7b38 Upgrade dependencies in compatible ranges (#524) 2019-12-14 16:56:02 -03:00
Edward Jones 97b0bf8669 Upgrade to Babel 7 (#523)
* Run npx babel-upgrade, removing unnecessary stage0 plugins

* Move from babel-preset-node6 to @babel/preset-env

* Update nyc to work with updated babel
2019-12-14 16:49:03 -03:00
Nick Savchenko 21985c6d97 Add ignoreUsers.discordIds option to ignore Discorders by ID (#508)
* Add option to ignore discord users by id

* Discord ids are strings. Add ignore by discord id example to README
2019-12-14 15:52:21 -03:00
Edward Jones 11a5347006 Support a basic image on Docker Hub (#498)
* Revert "Revert "Merge branch 'docker-test'""

This reverts commit 2b4bcb1136.

* Use node 12 for docker image

* More thoroughly limit deploy to master pushes

* Use discordirc/discord-irc repo for Docker

* Tag git commit image for Docker hub

* Add .dockerignore
2019-12-14 15:36:36 -03:00
Edward Jones 5d31e2d16b Upgrade simple-markdown per npm audit fix (#522) 2019-12-14 14:46:58 -03:00
Edward Jones 681cd40056 Add support for Node 13, drop automated testing for Node 6 (#521)
* Support Node 13

* Drop automated testing for Node 6
2019-12-14 14:35:26 -03:00
Edward Jones 8a6dec41c6 Rename ircPreventMention to parallelPingFix (#520) 2019-12-14 14:31:12 -03:00
dependabot[bot] 4e807ca34d Bump eslint-utils from 1.3.1 to 1.4.3 (#516)
Bumps [eslint-utils](https://github.com/mysticatea/eslint-utils) from 1.3.1 to 1.4.3.
- [Release notes](https://github.com/mysticatea/eslint-utils/releases)
- [Commits](https://github.com/mysticatea/eslint-utils/compare/v1.3.1...v1.4.3)

Signed-off-by: dependabot[bot] <support@github.com>
2019-12-14 13:41:10 -03:00
Edward Jones 221d42a44b Upgrade sinon to ^7.4.1, sinon-chai to ^3.3.0 (#503)
* Upgrade sinon to ^5.1.1 and sinon-chai to ^3.1.0

* Upgrade sinon to ^6.3.5

* Upgrade sinon to ^7.4.1
2019-08-08 13:03:05 -03:00
Doppins c28151a30c [Doppins] Upgrade dependency discord.js to 11.5.1 (#488)
* Upgrade dependency discord.js to 11.5.0

* Upgrade dependency discord.js to 11.5.1

* Update package-lock.json
2019-08-08 12:38:19 -03:00
Edward Jones d528aedb6d Add ircPreventMention (#502)
* Add ircPreventMention

(This adds a zero-width character to nicknames to prevent IRC-side pings.)

Original commits:

- Add ircPreventMention code

- Document ircPreventMention

- Make ircNickColor wrap the displayUsername

- Fix build errors

- Default ircPreventMention to false

* Correct that ircPreventMention is off by default

* Test ircPreventMention

Only tests turning it on; extant tests ensure it's off by default.
2019-08-08 12:06:07 -03:00
George 70e88498de Add a customizable fallback URL for webhook avatars (#419)
Adds a format option for a default avatar URL to send if the user isn't found on IRC. Takes the user's nickname as an argument so it can be passed to a web service.

Fixes #383.
2019-08-08 11:35:11 -03:00
Edward Jones 4f10b60c89 2.7.2 2019-08-08 11:14:52 -03:00
Edward Jones a719185cc9 Bump lint config versions for eslint 5 (#500)
Fixes deprecation warning upon lint
2019-08-08 11:00:01 -03:00
Edward Jones c9ccd7b473 Run all test/*.test.js files (#501)
Somehow, upgrading either mocha or nyc (?) appears to make
the current test command skip test/bot-events.test.js.
I do not know why, but quoting it (so mocha globs it
instead) seems to fix this.

(This happened between 2.7.0 and 2.7.1.)
2019-08-05 20:21:26 -03:00
Edward Jones 4196726799 Fix lodash security vulnerability 2019-08-05 18:57:01 -03:00
Edward Jones fc9f62a7e7 Add support for Node 10 and 12 (#499) 2019-08-05 00:27:59 +02:00
Edward Jones 3b362d8698 Defer to discord for disabling at-everyone in webhooks (#497)
Discord already has permissions for whether the bot can ping everyone,
and the webhook send method has a `disableEveryone` option, so let's
defer to the inbuilt Discord permission for this.

When permissions can't be found (shouldn't happen), defaults to
disabling the pings.

Fixes #494
2019-08-04 23:39:27 +02:00
Edward Jones 2b4bcb1136 Revert "Merge branch 'docker-test'"
This reverts commit 28fc86826b, reversing
changes made to ef553d37e2.
2019-08-01 17:44:52 +02:00
Edward Jones 28fc86826b Merge branch 'docker-test' 2019-08-01 17:37:08 +02:00
Edward Jones 8cf95fff4f Skip whole deploy step on non-master branches
The deploy section of the deploy phase used to skip anyway,
but it still ran a worker to get to the point where it decided
to skip. This stops it earlier.
2019-08-01 17:35:33 +02:00
Edward Jones 45a898ae67 Use top-level Dockerfile to build image 2019-08-01 17:27:37 +02:00
Edward Jones 492629816e Skip coverage reporting step in deployment 2019-08-01 17:12:48 +02:00
Edward Jones 98c6286416 Skip testing in deploy phase 2019-08-01 17:03:55 +02:00
Edward Jones 977b337a53 Move docker files, remove docker-compose, amend README
The Dockerfile references the code locally, so this move fixes
its `COPY . /bot` line.

docker-compose is primarily for combining container services,
and doesn't add much here, so we're removing it for now.

The README has been updated to explain how to run discord-irc
with a specific configuration file through Docker (locally
and from Docker Hub).
2019-08-01 16:49:56 +02:00
Edward Jones aac3fa677f Use a proper deploy stage for travis pushing to docker 2019-08-01 16:21:05 +02:00
Gareth Coles 5c18507ffd Docker: Build for every commit 2019-08-01 15:12:44 +01:00
Gareth Coles 9387bca2ce Merge branch 'master' into master 2019-07-29 09:45:41 +01:00
Martin Ek ef553d37e2 2.7.1 2019-06-15 19:21:20 -07:00
Martin Ek 3e6aa324f2 Update some dependencies 2019-06-15 19:20:24 -07:00
Martin Ek df3bf91465 Update simple-markdown 2019-06-15 19:15:54 -07:00
Martin Ek 68678fb134 2.7.0 2019-04-02 22:19:46 -07:00
Martin Ek 87608f3e99 Upgrade some dependencies 2019-03-16 23:30:19 +00:00
Martin Ek 5ea6abbe86 Add tests for webhook usernames 2019-03-16 23:29:59 +00:00
Martin Ek 15f2382639 Merge pull request #454 from simnalamburt/patch-1
Update bot.js
2019-03-16 23:16:11 +00:00
Martin Ek 32b12b3096 Merge pull request #476 from Throne3d/add/460-channel-mentions
Add channel mentions, similar to user mentions
2019-03-16 23:13:40 +00:00
Edward Jones 0e21e679fd Test bot converts same-guild channel mentions 2019-03-13 15:10:22 -03:00
Edward Jones 38e7f1fe3a Fix addRole and addEmoji stubs, add addTextChannel stub 2019-03-13 15:10:00 -03:00
Edward Jones d07ac66e39 Convert channel mentions similar to user mentions 2019-03-13 15:09:22 -03:00
Martin Ek 5e3a8e201b Merge pull request #470 from rdb/patch-1
Match IRC-style mentions at beginning of message
2019-03-05 00:49:04 +00:00
rdb ba505edd32 Add unit tests for IRC-style initial mentions (#470) 2019-03-04 15:47:48 +01:00
rdb d7d3bd6f67 Match IRC-style mentions at beginning of message 2019-02-11 13:54:26 +01:00
Gareth Coles 05e7ccd1eb Docker: Less layers (Thanks @3stadt) 2018-12-25 23:08:10 +00:00
Gareth Coles cb4979b0f8 Merge branch 'master' into master 2018-12-25 23:06:43 +00:00
Hyeon Kim d10606106f Update bot.js
Username must be between 2 and 32 in length, even in the webhook.

Reference:
  https://discordapp.com/developers/docs/resources/user#usernames-and-nicknames
2018-12-11 22:40:55 +09:00
James Ward 308766163a use util.inspect for logger instead of json.stringify (#453)
JSON.stringify can't represent every value from our application
passed to logger, but util.inspect can.

this fixes #446
2018-12-09 00:25:16 +00:00
James Ward ebee361e2d use non-deprecated form of Array.find (#449)
* use non-deprecated form of `Array.find`

The older form emitted a deprecation warning in nodejs, cluttering up the log.

* refactor to make lines shorter

required by code style guidelines
2018-11-22 18:28:18 +00:00
Martin Ek 4690e69bd3 2.6.2 2018-09-19 00:27:00 +01:00
Martin Ek 4f3c4365c6 Use bools in readme 2018-09-19 00:26:14 +01:00
Mikaela Suomalainen 1a8685d26d README.md: add SSL & SASL examples (#436)
[CI SKIP]
2018-09-19 00:25:42 +01:00
Martin Ek f8ad2d2fa7 Define a custom winston logger 2018-09-19 00:24:52 +01:00
Martin Ek d7c99e49af Update package-lock.json 2018-09-19 00:10:28 +01:00
Martin Ek 9a98f6da95 Merge branch 'doppins/discord.js-equals-11.4.0' 2018-09-19 00:09:13 +01:00
Martin Ek 877212dd0c Update package-lock.json 2018-09-19 00:08:30 +01:00
Martin Ek 1fd15b9f94 Merge branch 'master' into doppins/discord.js-equals-11.4.0 2018-09-18 23:49:49 +01:00
Doppins 21eda4ef53 Upgrade dependency irc-colors to 1.4.3 (#422) 2018-09-18 23:48:47 +01:00
Doppins b8c069ed12 [Doppins] Upgrade dependency commander to 2.18.0 (#418)
* Upgrade dependency commander to 2.16.0

* Upgrade dependency commander to 2.17.0

* Upgrade dependency commander to 2.17.1

* Upgrade dependency commander to 2.18.0
2018-09-18 23:48:37 +01:00
Doppins fe1aea55b1 Upgrade dependency irc-upd to 0.10.0 (#411) 2018-09-18 23:47:46 +01:00
Doppins dec2ea9379 [Doppins] Upgrade dependency winston to 3.1.0 (#410)
* Upgrade dependency winston to 2.4.3

* Upgrade dependency winston to 3.0.0

* Upgrade dependency winston to 2.4.4

* Upgrade dependency winston to 3.0.0

* Upgrade dependency winston to 3.0.1

* Upgrade dependency winston to 3.1.0
2018-09-18 23:47:29 +01:00
Doppins 9383b1bcf4 [Doppins] Upgrade dependency nyc to ^13.0.1 (#406)
* Upgrade dependency nyc to ^12.0.1

* Upgrade dependency nyc to ^12.0.2

* Upgrade dependency nyc to ^13.0.1
2018-09-18 23:47:17 +01:00
Doppins c5e7b13866 [Doppins] Upgrade dependency simple-markdown to 0.4.2 (#401)
* Upgrade dependency simple-markdown to 0.4.1

* Upgrade dependency simple-markdown to 0.4.2
2018-09-18 23:46:59 +01:00
Doppins 02264093f8 Upgrade dependency discord.js to 11.4.2 2018-08-13 14:22:50 +00:00
Doppins c977943ff8 Upgrade dependency discord.js to 11.4.1 2018-08-12 21:15:51 +00:00
Doppins c75031c67f Upgrade dependency discord.js to 11.4.0 2018-08-09 18:03:41 +00:00
Gareth Coles 2dcd6c97fc Fix dumb copy-paste mistake in build.sh 2018-07-20 09:52:23 +01:00
Gareth Coles 3d57250883 Update build.sh env vars for Travis 2018-07-20 09:44:49 +01:00
Gareth Coles 1f9f6bda6f Add Docker container files and documentation 2018-07-20 09:42:34 +01:00
26 changed files with 6097 additions and 5040 deletions
+17 -1
View File
@@ -1,3 +1,19 @@
{
"presets": ["node6", "stage-0"]
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "12"
}
}
]
],
"env": {
"test": {
"plugins": [
"istanbul"
]
}
}
}
+20
View File
@@ -0,0 +1,20 @@
.git
# Coverage directory used by tools like istanbul
coverage
.nyc_output
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
node_modules
# Environment variables and configuration
.env
.environment
config.json
# Build
dist/
+5 -2
View File
@@ -1,16 +1,19 @@
{
"extends": "airbnb-base",
"parser": "babel-eslint",
"parser": "@babel/eslint-parser",
"env": {
"mocha": true,
"node": true
},
"rules": {
"arrow-parens": 0,
"global-require": 0,
"comma-dangle": 0,
"func-names": 0,
"import/no-extraneous-dependencies": [2, { "devDependencies": true }],
"import/prefer-default-export": 0,
"import/no-dynamic-require": 0
"import/no-dynamic-require": 0,
"operator-linebreak": 0,
"implicit-arrow-linebreak": 0
}
}
+26
View File
@@ -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
+49
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "@istanbuljs/nyc-config-babel"
}
-6
View File
@@ -1,6 +0,0 @@
language: node_js
after_success:
- npm run report
node_js:
- '6'
- '8'
+61 -2
View File
@@ -1,6 +1,65 @@
# 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))
* Update acorn to 7.1.1 - [#534](https://github.com/reactiflux/discord-irc/pull/534)
* Remove code coverage instrumentation from `dist/` files - [#536](https://github.com/reactiflux/discord-irc/pull/536)
## [2.8.0] - 2019-12-14
### Added
* `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 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)
### Fixed
* Upgrade various dependencies: babel, commander, coveralls, discord.js, eslint, mocha, simple-markdown, sinon, sinon-chai - [#488](https://github.com/reactiflux/discord-irc/pull/488), [#503](https://github.com/reactiflux/discord-irc/pull/503), [#522](https://github.com/reactiflux/discord-irc/pull/522), [#523](https://github.com/reactiflux/discord-irc/pull/523), [#524](https://github.com/reactiflux/discord-irc/pull/524/files), [#525](https://github.com/reactiflux/discord-irc/pull/525)
## [2.7.2] - 2019-08-08
### Fixed
* Defer to discord permissions for allowing `@everyone` and `@here` in webhook messages - [#497](https://github.com/reactiflux/discord-irc/pull/497)
* Support Node 10 and 12 - [#499](https://github.com/reactiflux/discord-irc/pull/499)
* Upgrade dependencies
* Tests: Fix lint config deprecation - [#500](https://github.com/reactiflux/discord-irc/pull/500)
* Tests: Ensure all tests are run in dev environment - [#501](https://github.com/reactiflux/discord-irc/pull/501)
## [2.7.1] - 2019-06-15
### Changed
* Upgraded dependencies.
## [2.7.0] - 2019-04-02
### Changed
* Convert channel mentions to codified mentions (thanks to [Throne3d](https://github.com/reactiflux/discord-irc/pull/476)).
* Match IRC style mentions at the beginning of message (thanks to [rdb](https://github.com/reactiflux/discord-irc/pull/470)).
* Upgraded dependencies.
## [2.6.2] - 2018-09-19
### Changed
* Upgraded dependencies.
## [2.6.1] - 2018-05-11
### Changed
* Upgraded dependencies.
@@ -16,13 +75,13 @@ can appear as regular Discord messages:
![discord-webhook](http://i.imgur.com/lNeJIUI.jpg)
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"
}
```
+15
View File
@@ -0,0 +1,15 @@
FROM node:12-alpine
ENV LIBRARY_PATH=/lib:/usr/lib
RUN mkdir /bot
COPY . /bot
WORKDIR /bot
RUN apk add --update tini && \
npm install && \
npm run build && \
mkdir /config
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["npm", "start", "--", "--config", "/config/config.json"]
+2
View File
@@ -0,0 +1,2 @@
build:
docker-compose -f docker-compose.yml up -d --build --remove-orphans
+37 -9
View File
@@ -1,12 +1,13 @@
# discord-irc [![Build Status](https://travis-ci.org/reactiflux/discord-irc.svg?branch=master)](https://travis-ci.org/reactiflux/discord-irc) [![Coverage Status](https://coveralls.io/repos/github/reactiflux/discord-irc/badge.svg?branch=master)](https://coveralls.io/github/reactiflux/discord-irc?branch=master)
[![Coverage Status](https://coveralls.io/repos/github/reactiflux/discord-irc/badge.svg?branch=main)](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
![discord-irc](http://i.imgur.com/oI6iCrf.gif)
## 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
following the instructions [here](https://github.com/reactiflux/discord-irc#configuration).
@@ -37,6 +38,24 @@ import config from './config.json';
discordIRC(config);
```
## Docker
As an alternative to running discord-irc directly on your machine, we provide a [Docker container image](https://hub.docker.com/r/discordirc/discord-irc).
After creating a configuration file, you can fetch the image from Docker Hub and run it with the following command:
```bash
docker run -v /path/to/config:/config/config.json discordirc/discord-irc
```
If you've checked out the repository already, you can build the Docker image locally and run that instead:
```bash
docker build -t discord-irc .
docker run -v /path/to/config:/config/config.json discord-irc
```
Note that the path to the config file on the host (`/path/to/config`) _must_ be a valid absolute path to a config file.
Otherwise, you may get the error "illegal operation on a directory".
## Configuration
First you need to create a Discord bot user, which you can do by following the instructions [here](https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token).
@@ -69,30 +88,39 @@ First you need to create a Discord bot user, which you can do by following the i
},
"ircOptions": { // Optional node-irc options
"floodProtection": false, // On by default
"floodProtectionDelay": 1000 // 500 by default
"floodProtectionDelay": 1000, // 500 by default
"port": "6697", // 6697 by default
"secure": true, // enable SSL, false by default
"sasl": true, // false by default
"username": "test", // nodeirc by default
"password": "p455w0rd" // empty by default
},
"format": { // Optional custom formatting options
// Patterns, represented by {$patternName}, are replaced when sending messages
"commandPrelude": "Command sent by {$nickname}", // Message sent before a command
"ircText": "<{$displayUsername}> {$text}", // When sending a message to IRC
"urlAttachment": "<{$displayUsername}> {$attachmentURL}", // When sending a Discord attachment to IRC
"discord": "**<{$author}>** {$withMentions}" // When sending a message to Discord
"discord": "**<{$author}>** {$withMentions}", // When sending a message to Discord
// Other patterns that can be used:
// {$discordChannel} (e.g. #general)
// {$ircChannel} (e.g. #irc)
"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):
"commandCharacters": ["!", "."],
"ircStatusNotices": true, // Enables notifications in Discord when people join/part in the relevant IRC channel
"ignoreUsers": {
"irc": ["irc_nick1", "irc_nick2"], // Ignore specified IRC nicks and do not send their messages to Discord.
"discord": ["discord_nick1", "discord_nick2"] // Ignore specified Discord nicks and do not send their messages to IRC.
"discord": ["discord_nick1", "discord_nick2"], // Ignore specified Discord nicks and do not send their messages to IRC.
"discordIds": ["198528216523210752"] // Ignore specified Discord ids and do not send their messages to IRC.
},
// List of webhooks per channel
"webhooks": {
"#discord": "https://discordapp.com/api/webhooks/id/token"
"#discord": "https://discord.com/api/webhooks/id/token"
}
}
]
@@ -109,13 +137,13 @@ can appear as regular Discord messages:
![discord-webhook](http://i.imgur.com/lNeJIUI.jpg)
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"
}
```
+10
View File
@@ -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
+94 -35
View File
@@ -1,13 +1,17 @@
import _ from 'lodash';
import irc from 'irc-upd';
import logger from 'winston';
import discord from 'discord.js';
import logger from './logger';
import { ConfigurationError } from './errors';
import { validateChannelMapping } from './validators';
import { formatFromDiscordToIRC, formatFromIRCToDiscord } from './formatting';
// Usernames need to be between 2 and 32 characters for webhooks:
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;
@@ -25,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;
@@ -33,6 +40,8 @@ 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;
this.announceSelfJoin = options.announceSelfJoin;
@@ -42,6 +51,7 @@ class Bot {
this.ignoreUsers = options.ignoreUsers || {};
this.ignoreUsers.irc = this.ignoreUsers.irc || [];
this.ignoreUsers.discord = this.ignoreUsers.discord || [];
this.ignoreUsers.discordIds = this.ignoreUsers.discordIds || [];
// "{$keyName}" => "variableValue"
// author/nickname: nickname of the user who sent the message
@@ -55,6 +65,7 @@ class Bot {
// attachmentURL: the URL of the attachment (only applicable in formatURLAttachment)
this.formatIRCText = this.format.ircText || '<{$displayUsername}> {$text}';
this.formatURLAttachment = this.format.urlAttachment || '<{$displayUsername}> {$attachmentURL}';
// "{$keyName}" => "variableValue"
// side: "Discord" or "IRC"
if ('commandPrelude' in this.format) {
@@ -67,6 +78,10 @@ class Bot {
// withMentions: text with appropriate mentions reformatted
this.formatDiscord = this.format.discord || '**<{$author}>** {$withMentions}';
// "{$keyName} => "variableValue"
// nickname: nickame of IRC message sender
this.formatWebhookAvatarURL = this.format.webhookAvatarURL;
// Keep track of { channel => [list, of, usernames] } for ircStatusNotices
this.channelUsers = {};
@@ -122,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');
@@ -247,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;
}
@@ -258,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';
})
@@ -286,8 +306,12 @@ class Bot {
return this.ignoreUsers.irc.some(i => i.toLowerCase() === user.toLowerCase());
}
ignoredDiscordUser(user) {
return this.ignoreUsers.discord.some(i => i.toLowerCase() === user.toLowerCase());
ignoredDiscordUser(discordUser) {
const ignoredName = this.ignoreUsers.discord.some(
i => i.toLowerCase() === discordUser.username.toLowerCase()
);
const ignoredId = this.ignoreUsers.discordIds.some(i => i === discordUser.id);
return ignoredName || ignoredId;
}
static substitutePattern(message, patternMapping) {
@@ -302,7 +326,7 @@ class Bot {
) return;
// Do not send to IRC if this user is on the ignore list.
if (this.ignoredDiscordUser(author.username)) {
if (this.ignoredDiscordUser(author)) {
return;
}
@@ -316,9 +340,16 @@ class Bot {
const nickname = Bot.getDiscordNicknameOnServer(author, fromGuild);
let text = this.parseText(message);
let displayUsername = nickname;
if (this.parallelPingFix) {
// Prevent users of both IRC and Discord from
// being mentioned in IRC when they talk in Discord.
displayUsername = `${displayUsername.slice(0, 1)}\u200B${displayUsername.slice(1)}`;
}
if (this.ircNickColor) {
const colorIndex = (nickname.charCodeAt(0) + nickname.length) % NICK_COLORS.length;
displayUsername = irc.colors.wrap(NICK_COLORS[colorIndex], nickname);
const colorIndex = (nickname.charCodeAt(0) + nickname.length) % this.ircNickColors.length;
displayUsername = irc.colors.wrap(this.ircNickColors[colorIndex], displayUsername);
}
const patternMap = {
@@ -367,9 +398,15 @@ class Bot {
const discordChannelName = this.invertedMapping[ircChannel.toLowerCase()];
if (discordChannelName) {
// #channel -> channel before retrieving and select only text channels:
const discordChannel = discordChannelName.startsWith('#') ? this.discord.channels
.filter(c => c.type === 'text')
.find('name', discordChannelName.slice(1)) : this.discord.channels.get(discordChannelName);
let discordChannel = null;
if (this.discord.channels.cache.has(discordChannelName)) {
discordChannel = this.discord.channels.cache.get(discordChannelName);
} else if (discordChannelName.startsWith('#')) {
discordChannel = this.discord.channels.cache
.filter(c => c.type === 'text')
.find(c => c.name === discordChannelName.slice(1));
}
if (!discordChannel) {
logger.info(
@@ -389,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) {
@@ -408,9 +445,15 @@ class Bot {
users = guildMembers.filter(findByNicknameOrUsername(false));
}
// No matching user or more than one => no avatar
// No matching user or more than one => default avatar
if (users && users.size === 1) {
return users.first().user.avatarURL;
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
if (this.formatWebhookAvatarURL) {
return Bot.substitutePattern(this.formatWebhookAvatarURL, { nickname: nick });
}
return null;
}
@@ -464,26 +507,28 @@ 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 =>
Bot.caseComp(x.user.username, username.toUpperCase())
const user = guild.members.cache.find(x =>
Bot.caseComp(x.user.username, username)
&& x.user.discriminator === discriminator);
if (user) return user;
return match;
}).replace(/@([^\s]+)/g, (match, reference) => {
}).replace(/^([^@\s:,]+)[:,]|@([^\s]+)/g, (match, startRef, atRef) => {
const reference = startRef || atRef;
// 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.
@@ -508,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);
});
@@ -525,21 +570,35 @@ 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;
}).replace(/#([^\s#@'!?,.]+)/g, (match, channelName) => {
// channel names can't contain spaces, #, @, ', !, ?, , or .
// (based on brief testing. they also can't contain some other symbols,
// but these seem likely to be common around channel references)
// discord matches channel names case insensitively
const chan = guild.channels.cache.find(x => Bot.caseComp(x.name, channelName));
return chan || match;
});
// Webhooks first
const webhook = this.findWebhook(channel);
if (webhook) {
logger.debug('Sending message to Discord via webhook', withMentions, channel, '->', `#${discordChannel.name}`);
const permissions = discordChannel.permissionsFor(this.discord.user);
let canPingEveryone = false;
if (permissions) {
canPingEveryone = permissions.has(discord.Permissions.FLAGS.MENTION_EVERYONE);
}
const avatarURL = this.getDiscordAvatar(author, channel);
webhook.client.sendMessage(withMentions, {
username: author,
text,
avatarURL
const username = _.padEnd(author.substring(0, USERNAME_MAX_LENGTH), USERNAME_MIN_LENGTH, '_');
webhook.client.send(withMentions, {
username,
avatarURL,
disableMentions: canPingEveryone ? 'none' : 'everyone',
}).catch(logger.error);
return;
}
+4 -3
View File
@@ -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') ?
-6
View File
@@ -1,13 +1,7 @@
#!/usr/bin/env node
import logger from 'winston';
import { createBots } from './helpers';
/* istanbul ignore next */
if (process.env.NODE_ENV === 'development') {
logger.level = 'debug';
}
/* istanbul ignore next */
if (!module.parent) {
require('./cli').default();
+27
View File
@@ -0,0 +1,27 @@
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 splat = info[Symbol.for('splat')] || [];
const stringifiedRest = splat.length > 0 ? ` ${splat.map(simpleInspect).join(' ')}` : '';
const padding = (info.padding && info.padding[info.level]) || '';
return `${info.timestamp} ${info.level}:${padding} ${info.message}${stringifiedRest}`;
}
const logger = winston.createLogger({
transports: [new winston.transports.Console()],
level: process.env.NODE_ENV === 'development' ? 'debug' : 'info',
format: format.combine(
format.colorize(),
format.timestamp(),
format.printf(formatter),
)
});
export default logger;
+5263 -4773
View File
File diff suppressed because it is too large Load Diff
+29 -27
View File
@@ -1,6 +1,6 @@
{
"name": "discord-irc",
"version": "2.6.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",
@@ -27,8 +27,8 @@
"build": "babel lib --out-dir dist",
"prepare": "npm run build",
"lint": "eslint . --ignore-path .gitignore",
"coverage": "nyc --require babel-core/register _mocha -- test/*.test.js",
"report": "nyc report --reporter=text-lcov | coveralls",
"coverage": "cross-env NODE_ENV=test nyc --require @babel/register mocha -- 'test/*.test.js'",
"save-coverage": "nyc report --reporter=lcov",
"test": "npm run lint && npm run coverage"
},
"author": {
@@ -36,31 +36,33 @@
},
"license": "MIT",
"dependencies": {
"check-env": "1.3.0",
"commander": "2.15.1",
"discord.js": "11.3.2",
"irc-colors": "1.4.2",
"commander": "^7.2.0",
"discord.js": "^12.5.3",
"irc-colors": "1.5.0",
"irc-formatting": "1.0.0-rc3",
"irc-upd": "0.9.0",
"lodash": "^4.17.4",
"simple-markdown": "0.4.0",
"strip-json-comments": "2.0.1",
"winston": "2.4.2"
"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": "^6.6.5",
"babel-core": "^6.7.4",
"babel-eslint": "^8.0.1",
"babel-preset-node6": "^11.0.0",
"babel-preset-stage-0": "^6.5.0",
"chai": "^4.0.2",
"coveralls": "^3.0.0",
"eslint": "^4.8.0",
"eslint-config-airbnb-base": "^12.0.1",
"eslint-plugin-import": "^2.7.0",
"mocha": "^5.0.0",
"nyc": "^11.0.3",
"sinon": "^4.0.1",
"sinon-chai": "^2.8.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"
}
}
+6 -4
View File
@@ -4,9 +4,10 @@ import sinonChai from 'sinon-chai';
import sinon from 'sinon';
import irc from 'irc-upd';
import discord from 'discord.js';
import logger from 'winston';
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';
@@ -14,7 +15,7 @@ chai.should();
chai.use(sinonChai);
describe('Bot Events', function () {
const sandbox = sinon.sandbox.create({
const sandbox = sinon.createSandbox({
useFakeTimers: false,
useFakeServer: false
});
@@ -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();
});
+362 -153
View File
@@ -3,8 +3,8 @@ import chai from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import irc from 'irc-upd';
import logger from 'winston';
import discord from 'discord.js';
import logger from '../lib/logger';
import Bot from '../lib/bot';
import createDiscordStub from './stubs/discord-stub';
import ClientStub from './stubs/irc-client-stub';
@@ -12,59 +12,61 @@ import createWebhookStub from './stubs/webhook-stub';
import config from './fixtures/single-test-config.json';
import configMsgFormatDefault from './fixtures/msg-formats-default.json';
const { expect } = chai;
chai.should();
chai.use(sinonChai);
describe('Bot', function () {
const sandbox = sinon.sandbox.create({
const sandbox = sinon.createSandbox({
useFakeTimers: false,
useFakeServer: false
});
const createGuildStub = () => ({
roles: new discord.Collection(),
members: new discord.Collection(),
emojis: new discord.Collection()
});
beforeEach(function () {
this.infoSpy = sandbox.stub(logger, 'info');
this.debugSpy = sandbox.stub(logger, 'debug');
this.errorSpy = sandbox.stub(logger, 'error');
this.sendStub = sandbox.stub();
this.discordUsers = new discord.Collection();
irc.Client = ClientStub;
this.guild = createGuildStub();
discord.Client = createDiscordStub(this.sendStub, this.guild, this.discordUsers);
discord.Client = createDiscordStub(this.sendStub);
ClientStub.prototype.say = sandbox.stub();
ClientStub.prototype.send = sandbox.stub();
ClientStub.prototype.join = sandbox.stub();
this.sendWebhookMessageStub = sandbox.stub();
discord.WebhookClient = createWebhookStub(this.sendWebhookMessageStub);
this.bot = new Bot(config);
this.bot.connect();
this.setCustomBot = conf => {
this.bot = new Bot(conf);
this.guild = this.bot.discord.guilds.cache.first();
this.bot.connect();
};
this.setCustomBot(config);
// modified variants of https://github.com/discordjs/discord.js/blob/stable/src/client/ClientDataManager.js
// (for easier stubbing)
this.addUser = function (user, member = null) {
const userObj = new discord.User(this.bot.discord, user);
const guildMember = Object.assign({}, member || user, { user: userObj });
// 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.bot.discord, 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.bot.discord, 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;
};
});
@@ -151,8 +153,7 @@ describe('Bot', function () {
it('should not color irc messages if the option is disabled', function () {
const text = 'testmessage';
const newConfig = { ...config, ircNickColor: false };
const bot = new Bot(newConfig);
bot.connect();
this.setCustomBot(newConfig);
const message = {
content: text,
mentions: { users: [] },
@@ -166,11 +167,33 @@ describe('Bot', function () {
guild: this.guild
};
bot.sendToIRC(message);
this.bot.sendToIRC(message);
const expected = `<${message.author.username}> ${text}`;
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 = {
@@ -339,6 +362,32 @@ describe('Bot', function () {
}
);
it('should break mentions when parallelPingFix is enabled', function () {
const newConfig = { ...config, parallelPingFix: true };
this.setCustomBot(newConfig);
const text = 'testmessage';
const username = 'otherauthor';
const brokenNickname = 'o\u200Btherauthor';
const message = {
content: text,
mentions: { users: [] },
channel: {
name: 'discord'
},
author: {
username,
id: 'not bot id'
},
guild: this.guild
};
this.bot.sendToIRC(message);
// Wrap in colors:
const expected = `<\u000304${brokenNickname}\u000f> ${text}`;
ClientStub.prototype.say.should.have.been.calledWith('#irc', expected);
});
it('should parse text from discord when sending messages', function () {
const text = '<#1234>';
const message = {
@@ -432,7 +481,7 @@ describe('Bot', function () {
this.bot.parseText(message).should.equal(':in_love:');
});
it('should convert user mentions from IRC', function () {
it('should convert user at-mentions from IRC', function () {
const testUser = this.addUser({ username: 'testuser', id: '123' });
const username = 'ircuser';
@@ -443,7 +492,40 @@ describe('Bot', function () {
this.sendStub.should.have.been.calledWith(expected);
});
it('should not convert user mentions from IRC if such user does not exist', function () {
it('should convert user colon-initial mentions from IRC', function () {
const testUser = this.addUser({ username: 'testuser', id: '123' });
const username = 'ircuser';
const text = 'testuser: hello!';
const expected = `**<${username}>** <@${testUser.id}> hello!`;
this.bot.sendToDiscord(username, '#irc', text);
this.sendStub.should.have.been.calledWith(expected);
});
it('should convert user comma-initial mentions from IRC', function () {
const testUser = this.addUser({ username: 'testuser', id: '123' });
const username = 'ircuser';
const text = 'testuser, hello!';
const expected = `**<${username}>** <@${testUser.id}> hello!`;
this.bot.sendToDiscord(username, '#irc', text);
this.sendStub.should.have.been.calledWith(expected);
});
it('should not convert user initial mentions from IRC mid-message', function () {
this.addUser({ username: 'testuser', id: '123' });
const username = 'ircuser';
const text = 'Hi there testuser, how goes?';
const expected = `**<${username}>** Hi there testuser, how goes?`;
this.bot.sendToDiscord(username, '#irc', text);
this.sendStub.should.have.been.calledWith(expected);
});
it('should not convert user at-mentions from IRC if such user does not exist', function () {
const username = 'ircuser';
const text = 'See you there @5pm';
const expected = `**<${username}>** See you there @5pm`;
@@ -452,6 +534,15 @@ describe('Bot', function () {
this.sendStub.should.have.been.calledWith(expected);
});
it('should not convert user initial mentions from IRC if such user does not exist', function () {
const username = 'ircuser';
const text = 'Agreed, see you then.';
const expected = `**<${username}>** Agreed, see you then.`;
this.bot.sendToDiscord(username, '#irc', text);
this.sendStub.should.have.been.calledWith(expected);
});
it('should convert multiple user mentions from IRC', function () {
const testUser = this.addUser({ username: 'testuser', id: '123' });
const anotherUser = this.addUser({ username: 'anotheruser', id: '124' });
@@ -475,6 +566,19 @@ describe('Bot', function () {
this.sendStub.should.have.been.calledWith(expected);
});
it('should convert channel mentions from IRC', function () {
this.guild.addTextChannel({ id: '1235', name: 'testchannel' });
this.guild.addTextChannel({ id: '1236', name: 'channel-compliqué' });
const otherGuild = this.bot.discord.createGuildStub({ id: '2' });
otherGuild.addTextChannel({ id: '1237', name: 'foreignchannel' });
const username = 'ircuser';
const text = "Here is a broken #channelname, a working #testchannel, #channel-compliqué, an irregular case #TestChannel and another guild's #foreignchannel";
const expected = `**<${username}>** Here is a broken #channelname, a working <#1235>, <#1236>, an irregular case <#1235> and another guild's #foreignchannel`;
this.bot.sendToDiscord(username, '#irc', text);
this.sendStub.should.have.been.calledWith(expected);
});
it('should convert newlines from discord', function () {
const message = {
mentions: { users: [] },
@@ -507,7 +611,7 @@ describe('Bot', function () {
});
it('should support multi-character command prefixes', function () {
const bot = new Bot({ ...config, commandCharacters: ['@@'] });
this.setCustomBot({ ...config, commandCharacters: ['@@'] });
const text = '@@test command';
const message = {
content: text,
@@ -521,9 +625,8 @@ describe('Bot', function () {
},
guild: this.guild
};
bot.connect();
bot.sendToIRC(message);
this.bot.sendToIRC(message);
ClientStub.prototype.say.getCall(0).args.should.deep.equal([
'#irc', 'Command sent from Discord by test:'
]);
@@ -542,11 +645,10 @@ describe('Bot', function () {
it('should use nickname instead of username when available', function () {
const text = 'testmessage';
const newConfig = { ...config, ircNickColor: false };
const bot = new Bot(newConfig);
this.setCustomBot(newConfig);
const id = 'not bot id';
const nickname = 'discord-nickname';
this.guild.members.set(id, { nickname });
bot.connect();
this.guild.members.cache.set(id, { nickname });
const message = {
content: text,
mentions: { users: [] },
@@ -560,7 +662,7 @@ describe('Bot', function () {
guild: this.guild
};
bot.sendToIRC(message);
this.bot.sendToIRC(message);
const expected = `<${nickname}> ${text}`;
ClientStub.prototype.say.should.have.been.calledWith('#irc', expected);
});
@@ -696,55 +798,7 @@ describe('Bot', function () {
});
it('should successfully send messages with default config', function () {
const bot = new Bot(configMsgFormatDefault);
bot.connect();
bot.sendToDiscord('testuser', '#irc', 'test message');
this.sendStub.should.have.been.calledOnce;
const message = {
content: 'test message',
mentions: { users: [] },
channel: {
name: 'discord'
},
author: {
username: 'otherauthor',
id: 'not bot id'
},
guild: this.guild
};
bot.sendToIRC(message);
this.sendStub.should.have.been.calledOnce;
});
it('should not replace unmatched patterns', function () {
const format = { discord: '{$unmatchedPattern} stays intact: {$author} {$text}' };
const bot = new Bot({ ...configMsgFormatDefault, format });
bot.connect();
const username = 'testuser';
const msg = 'test message';
const expected = `{$unmatchedPattern} stays intact: ${username} ${msg}`;
bot.sendToDiscord(username, '#irc', msg);
this.sendStub.should.have.been.calledWith(expected);
});
it('should respect custom formatting for Discord', function () {
const format = { discord: '<{$author}> {$ircChannel} => {$discordChannel}: {$text}' };
const bot = new Bot({ ...configMsgFormatDefault, format });
bot.connect();
const username = 'test';
const msg = 'test @user <#1234>';
const expected = `<test> #irc => #discord: ${msg}`;
bot.sendToDiscord(username, '#irc', msg);
this.sendStub.should.have.been.calledWith(expected);
});
it('should successfully send messages with default config', function () {
this.bot = new Bot(configMsgFormatDefault);
this.bot.connect();
this.setCustomBot(configMsgFormatDefault);
this.bot.sendToDiscord('testuser', '#irc', 'test message');
this.sendStub.should.have.been.calledOnce;
@@ -767,8 +821,51 @@ describe('Bot', function () {
it('should not replace unmatched patterns', function () {
const format = { discord: '{$unmatchedPattern} stays intact: {$author} {$text}' };
this.bot = new Bot({ ...configMsgFormatDefault, format });
this.bot.connect();
this.setCustomBot({ ...configMsgFormatDefault, format });
const username = 'testuser';
const msg = 'test message';
const expected = `{$unmatchedPattern} stays intact: ${username} ${msg}`;
this.bot.sendToDiscord(username, '#irc', msg);
this.sendStub.should.have.been.calledWith(expected);
});
it('should respect custom formatting for Discord', function () {
const format = { discord: '<{$author}> {$ircChannel} => {$discordChannel}: {$text}' };
this.setCustomBot({ ...configMsgFormatDefault, format });
const username = 'test';
const msg = 'test @user <#1234>';
const expected = `<test> #irc => #discord: ${msg}`;
this.bot.sendToDiscord(username, '#irc', msg);
this.sendStub.should.have.been.calledWith(expected);
});
it('should successfully send messages with default config', function () {
this.setCustomBot(configMsgFormatDefault);
this.bot.sendToDiscord('testuser', '#irc', 'test message');
this.sendStub.should.have.been.calledOnce;
const message = {
content: 'test message',
mentions: { users: [] },
channel: {
name: 'discord'
},
author: {
username: 'otherauthor',
id: 'not bot id'
},
guild: this.guild
};
this.bot.sendToIRC(message);
this.sendStub.should.have.been.calledOnce;
});
it('should not replace unmatched patterns', function () {
const format = { discord: '{$unmatchedPattern} stays intact: {$author} {$text}' };
this.setCustomBot({ ...configMsgFormatDefault, format });
const username = 'testuser';
const msg = 'test message';
@@ -779,8 +876,7 @@ describe('Bot', function () {
it('should respect custom formatting for regular Discord output', function () {
const format = { discord: '<{$author}> {$ircChannel} => {$discordChannel}: {$text}' };
this.bot = new Bot({ ...configMsgFormatDefault, format });
this.bot.connect();
this.setCustomBot({ ...configMsgFormatDefault, format });
const username = 'test';
const msg = 'test @user <#1234>';
@@ -791,8 +887,7 @@ describe('Bot', function () {
it('should respect custom formatting for commands in Discord output', function () {
const format = { commandPrelude: '{$nickname} from {$ircChannel} sent command to {$discordChannel}:' };
this.bot = new Bot({ ...configMsgFormatDefault, format });
this.bot.connect();
this.setCustomBot({ ...configMsgFormatDefault, format });
const username = 'test';
const msg = '!testcmd';
@@ -804,8 +899,7 @@ describe('Bot', function () {
it('should respect custom formatting for regular IRC output', function () {
const format = { ircText: '<{$nickname}> {$discordChannel} => {$ircChannel}: {$text}' };
this.bot = new Bot({ ...configMsgFormatDefault, format });
this.bot.connect();
this.setCustomBot({ ...configMsgFormatDefault, format });
const message = {
content: 'test message',
mentions: { users: [] },
@@ -826,8 +920,7 @@ describe('Bot', function () {
it('should respect custom formatting for commands in IRC output', function () {
const format = { commandPrelude: '{$nickname} from {$discordChannel} sent command to {$ircChannel}:' };
this.bot = new Bot({ ...configMsgFormatDefault, format });
this.bot.connect();
this.setCustomBot({ ...configMsgFormatDefault, format });
const text = '!testcmd';
const message = {
@@ -851,8 +944,7 @@ describe('Bot', function () {
it('should respect custom formatting for attachment URLs in IRC output', function () {
const format = { urlAttachment: '<{$nickname}> {$discordChannel} => {$ircChannel}, attachment: {$attachmentURL}' };
this.bot = new Bot({ ...configMsgFormatDefault, format });
this.bot.connect();
this.setCustomBot({ ...configMsgFormatDefault, format });
const attachmentUrl = 'https://image/url.jpg';
const message = {
@@ -876,8 +968,7 @@ describe('Bot', function () {
it('should not bother with command prelude if falsy', function () {
const format = { commandPrelude: null };
this.bot = new Bot({ ...configMsgFormatDefault, format });
this.bot.connect();
this.setCustomBot({ ...configMsgFormatDefault, format });
const text = '!testcmd';
const message = {
@@ -916,68 +1007,165 @@ describe('Bot', function () {
this.bot.findWebhook('#ircwebhook').should.not.equal(null);
});
it('should prefer webhooks to send a message when possible', function () {
const newConfig = { ...config, webhooks: { '#discord': 'https://discordapp.com/api/webhooks/id/token' } };
const bot = new Bot(newConfig);
bot.connect();
bot.sendToDiscord('nick', '#irc', 'text');
this.sendWebhookMessageStub.should.have.been.called;
});
context('with enabled Discord webhook', function () {
this.beforeEach(function () {
const newConfig = { ...config, webhooks: { '#discord': 'https://discord.com/api/webhooks/id/token' } };
this.setCustomBot(newConfig);
});
it('should find a matching username, case sensitive, when looking for an avatar', function () {
const newConfig = { ...config, webhooks: { '#discord': 'https://discordapp.com/api/webhooks/id/token' } };
const bot = new Bot(newConfig);
bot.connect();
const userObj = { id: 123, username: 'Nick', avatar: 'avatarURL' };
const memberObj = { nickname: 'Different' };
this.addUser(userObj, memberObj);
this.bot.getDiscordAvatar('Nick', '#irc').should.equal('/avatars/123/avatarURL.png?size=2048');
});
it('should prefer webhooks to send a message', function () {
this.bot.sendToDiscord('nick', '#irc', 'text');
this.sendWebhookMessageStub.should.have.been.called;
});
it('should find a matching username, case insensitive, when looking for an avatar', function () {
const newConfig = { ...config, webhooks: { '#discord': 'https://discordapp.com/api/webhooks/id/token' } };
const bot = new Bot(newConfig);
bot.connect();
const userObj = { id: 124, username: 'nick', avatar: 'avatarURL' };
const memberObj = { nickname: 'Different' };
this.addUser(userObj, memberObj);
this.bot.getDiscordAvatar('Nick', '#irc').should.equal('/avatars/124/avatarURL.png?size=2048');
});
it('pads too short usernames', function () {
const text = 'message';
this.bot.sendToDiscord('n', '#irc', text);
this.sendWebhookMessageStub.should.have.been.calledWith(text, {
username: 'n_',
avatarURL: null,
disableMentions: 'everyone',
});
});
it('should find a matching nickname, case sensitive, when looking for an avatar', function () {
const newConfig = { ...config, webhooks: { '#discord': 'https://discordapp.com/api/webhooks/id/token' } };
const bot = new Bot(newConfig);
bot.connect();
const userObj = { id: 125, username: 'Nick', avatar: 'avatarURL' };
const memberObj = { nickname: 'Different' };
this.addUser(userObj, memberObj);
this.bot.getDiscordAvatar('Different', '#irc').should.equal('/avatars/125/avatarURL.png?size=2048');
});
it('slices too long usernames', function () {
const text = 'message';
this.bot.sendToDiscord('1234567890123456789012345678901234567890', '#irc', text);
this.sendWebhookMessageStub.should.have.been.calledWith(text, {
username: '12345678901234567890123456789012',
avatarURL: null,
disableMentions: 'everyone',
});
});
it('should not return an avatar with two matching usernames when looking for an avatar', function () {
const newConfig = { ...config, webhooks: { '#discord': 'https://discordapp.com/api/webhooks/id/token' } };
const bot = new Bot(newConfig);
bot.connect();
const userObj1 = { id: 126, username: 'common', avatar: 'avatarURL' };
const userObj2 = { id: 127, username: 'Nick', avatar: 'avatarURL' };
const memberObj1 = { nickname: 'Different' };
const memberObj2 = { nickname: 'common' };
this.addUser(userObj1, memberObj1);
this.addUser(userObj2, memberObj2);
chai.should().equal(this.bot.getDiscordAvatar('common', '#irc'), null);
});
it('does not ping everyone if user lacks permission', function () {
const text = 'message';
const permission = discord.Permissions.FLAGS.VIEW_CHANNEL
+ discord.Permissions.FLAGS.SEND_MESSAGES;
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',
avatarURL: null,
disableMentions: 'everyone',
});
});
it('should not return an avatar when no users match and should handle lack of nickname, when looking for an avatar', function () {
const newConfig = { ...config, webhooks: { '#discord': 'https://discordapp.com/api/webhooks/id/token' } };
const bot = new Bot(newConfig);
bot.connect();
const userObj1 = { id: 128, username: 'common', avatar: 'avatarURL' };
const userObj2 = { id: 129, username: 'Nick', avatar: 'avatarURL' };
const memberObj1 = {};
const memberObj2 = { nickname: 'common' };
this.addUser(userObj1, memberObj1);
this.addUser(userObj2, memberObj2);
chai.should().equal(this.bot.getDiscordAvatar('nonexistent', '#irc'), null);
it('sends @everyone messages if the bot has permission to do so', function () {
const text = 'message';
const permission = discord.Permissions.FLAGS.VIEW_CHANNEL
+ discord.Permissions.FLAGS.SEND_MESSAGES
+ discord.Permissions.FLAGS.MENTION_EVERYONE;
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',
avatarURL: null,
disableMentions: 'none',
});
});
const setupUser = base => {
const userObj = { id: 123, username: 'Nick', avatar: 'avatarURL' };
const memberObj = { nickname: 'Different' };
base.addUser(userObj, memberObj);
};
const setupCommonPair = base => {
const userObj1 = { id: 124, username: 'common', avatar: 'avatarURL' };
const userObj2 = { id: 125, username: 'diffUser', avatar: 'avatarURL' };
const memberObj1 = { nickname: 'diffNick' };
const memberObj2 = { nickname: 'common' };
base.addUser(userObj1, memberObj1);
base.addUser(userObj2, memberObj2);
};
context('when matching avatars', function () {
this.beforeEach(function () {
setupUser(this);
});
it('should match a user\'s username', function () {
this.bot.getDiscordAvatar('Nick', '#irc').should.equal('/avatars/123/avatarURL.png?size=128');
});
it('should match a user\'s username case insensitively', function () {
this.bot.getDiscordAvatar('nick', '#irc').should.equal('/avatars/123/avatarURL.png?size=128');
});
it('should match a user\'s nickname', function () {
this.bot.getDiscordAvatar('Different', '#irc').should.equal('/avatars/123/avatarURL.png?size=128');
});
it('should match a user\'s nickname case insensitively', function () {
this.bot.getDiscordAvatar('different', '#irc').should.equal('/avatars/123/avatarURL.png?size=128');
});
it('should only return matching users\' avatars', function () {
expect(this.bot.getDiscordAvatar('other', '#irc')).to.equal(null);
});
it('should return no avatar when there are multiple matches', function () {
setupCommonPair(this);
this.bot.getDiscordAvatar('diffUser', '#irc').should.not.equal(null);
this.bot.getDiscordAvatar('diffNick', '#irc').should.not.equal(null);
expect(this.bot.getDiscordAvatar('common', '#irc')).to.equal(null);
});
it('should handle users without nicknames', function () {
const userObj = { id: 124, username: 'nickless', avatar: 'nickless-avatar' };
const memberObj = {};
this.addUser(userObj, memberObj);
this.bot.getDiscordAvatar('nickless', '#irc').should.equal('/avatars/124/nickless-avatar.png?size=128');
});
it('should handle users without avatars', function () {
const userObj = { id: 124, username: 'avatarless' };
const memberObj = {};
this.addUser(userObj, memberObj);
expect(this.bot.getDiscordAvatar('avatarless', '#irc')).to.equal(null);
});
});
context('when matching avatars with fallback URL', function () {
this.beforeEach(function () {
const newConfig = { ...config, webhooks: { '#discord': 'https://discord.com/api/webhooks/id/token' }, format: { webhookAvatarURL: 'avatarFrom/{$nickname}' } };
this.setCustomBot(newConfig);
setupUser(this);
});
it('should use a matching user\'s avatar', function () {
this.bot.getDiscordAvatar('Nick', '#irc').should.equal('/avatars/123/avatarURL.png?size=128');
this.bot.getDiscordAvatar('nick', '#irc').should.equal('/avatars/123/avatarURL.png?size=128');
this.bot.getDiscordAvatar('Different', '#irc').should.equal('/avatars/123/avatarURL.png?size=128');
this.bot.getDiscordAvatar('different', '#irc').should.equal('/avatars/123/avatarURL.png?size=128');
});
it('should use fallback without matching user', function () {
this.bot.getDiscordAvatar('other', '#irc').should.equal('avatarFrom/other');
});
it('should use fallback when there are multiple matches', function () {
setupCommonPair(this);
this.bot.getDiscordAvatar('diffUser', '#irc').should.equal('/avatars/125/avatarURL.png?size=128');
this.bot.getDiscordAvatar('diffNick', '#irc').should.equal('/avatars/124/avatarURL.png?size=128');
this.bot.getDiscordAvatar('common', '#irc').should.equal('avatarFrom/common');
});
it('should use fallback for users without avatars', function () {
const userObj = { id: 124, username: 'avatarless' };
const memberObj = {};
this.addUser(userObj, memberObj);
this.bot.getDiscordAvatar('avatarless', '#irc').should.equal('avatarFrom/avatarless');
});
});
});
it(
@@ -1008,4 +1196,25 @@ describe('Bot', function () {
ClientStub.prototype.say.should.not.have.been.called;
}
);
it(
'should not send messages to IRC if Discord user is ignored by id',
function () {
const message = {
content: 'text',
mentions: { users: [] },
channel: {
name: 'discord'
},
author: {
username: 'vasya_pupkin',
id: '4499'
},
guild: this.guild
};
this.bot.sendToIRC(message);
ClientStub.prototype.say.should.not.have.been.called;
}
);
});
+1 -1
View File
@@ -11,7 +11,7 @@ chai.should();
chai.use(sinonChai);
describe('CLI', function () {
const sandbox = sinon.sandbox.create({
const sandbox = sinon.createSandbox({
useFakeTimers: false,
useFakeServer: false
});
+1 -1
View File
@@ -14,7 +14,7 @@ chai.should();
chai.use(sinonChai);
describe('Create Bots', function () {
const sandbox = sinon.sandbox.create({
const sandbox = sinon.createSandbox({
useFakeTimers: false,
useFakeServer: false
});
+3 -2
View File
@@ -17,10 +17,11 @@
"#withwebhook": "#ircwebhook"
},
"webhooks": {
"#withwebhook": "https://discordapp.com/api/webhooks/id/token"
"#withwebhook": "https://discord.com/api/webhooks/id/token"
},
"ignoreUsers": {
"irc": ["irc_ignored_user"],
"discord": ["discord_ignored_user"]
"discord": ["discord_ignored_user"],
"discordIds": ["4499"]
}
}
+55 -13
View File
@@ -3,33 +3,75 @@ import events from 'events';
import sinon from 'sinon';
import discord from 'discord.js';
export default function createDiscordStub(sendStub, guild, discordUsers) {
export default function createDiscordStub(sendStub) {
return class DiscordStub extends events.EventEmitter {
constructor() {
super();
this.user = {
id: 'testid'
};
this.channels = this.guildChannels();
this.channels = new discord.ChannelManager(this, []);
this.options = {
http: {
cdn: ''
}
};
this.users = discordUsers;
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();
}
guildChannels() {
const channels = new discord.Collection();
channels.set('1234', {
name: 'discord',
id: '1234',
type: 'text',
send: sendStub,
guild
});
return channels;
destroy() {}
addTextChannel(guild, textChannel) {
const textChannelData = {
type: discord.Constants.ChannelTypes.TEXT,
...textChannel
};
const textChannelObj = new discord.TextChannel(guild, textChannelData);
textChannelObj.send = sendStub;
const permissions = new discord.Collection();
textChannelObj.setPermissionStub = (user, perms) => permissions.set(user, perms);
textChannelObj.permissionsFor = user => permissions.get(user);
this.channels.cache.set(textChannelObj.id, textChannelObj);
return textChannelObj;
}
createGuildStub(guildData = {}) {
const guild = {
id: '1',
client: this,
addTextChannel: (textChannel) => {
const textChannelObj = this.addTextChannel(guild, textChannel);
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.cache.set(guild.id, guild);
if (guild.id === '1') {
guild.addTextChannel({
name: 'discord',
id: '1234',
});
}
return guild;
}
createRestStub() {
return {
cdn: discord.Constants.Endpoints.CDN(''),
};
}
login() {
+3
View File
@@ -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;
+4 -2
View File
@@ -6,9 +6,11 @@ export default function createWebhookStub(sendWebhookMessage) {
this.token = token;
}
sendMessage() {
sendWebhookMessage();
send(...args) {
sendWebhookMessage(...args);
return new Promise(() => {});
}
destroy() {}
};
}