Compare commits

..
120 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
Martin Ek b252f56658 2.6.1 2018-05-11 22:37:19 +02:00
Martin Ek 6a6a1ed547 Remove node-icu warning 2018-05-11 22:36:54 +02:00
Doppins 1c6146d603 Upgrade dependency simple-markdown to 0.4.0 (#391) 2018-05-11 22:35:45 +02:00
Doppins 20e76872ad Upgrade dependency winston to 2.4.2 (#387) 2018-05-11 22:35:25 +02:00
Doppins cb7dc2b75d Upgrade dependency irc-upd to 0.9.0 (#378) 2018-05-11 22:35:12 +02:00
Edward Jones f624134814 Fix parsing of animated emoji from Discord (#388)
(Fixes #384.)

On the Discord side of things, animated emoji get a prefixed 'a' to the
identifier string, so that instead of the normal emoji string:

`<:test:330064465703862273>`

you instead get:

`<a:test:330064465703862273>`

This commit fixes parseText to also process the animated form into `:test:`.
2018-04-23 12:02:10 +02:00
Doppins 22e3bece3c [Doppins] Upgrade dependency discord.js to 11.3.2 (#371)
* Upgrade dependency discord.js to 11.3.1

* Upgrade dependency discord.js to 11.3.2
2018-03-23 01:23:36 +01:00
Doppins 883c3ecd83 [Doppins] Upgrade dependency commander to 2.15.1 (#363)
* Upgrade dependency commander to 2.14.0

* Upgrade dependency commander to 2.14.1

* Upgrade dependency commander to 2.15.0

* Upgrade dependency commander to 2.15.1
2018-03-22 18:23:14 +01:00
Martin Ek a1e645626f Merge pull request #376 from reactiflux/doppins/simple-markdown-equals-0.3.3
[Doppins] Upgrade dependency simple-markdown to 0.3.3
2018-03-22 17:59:27 +01:00
Martin Ek f0afa90170 Merge pull request #374 from reactiflux/doppins/winston-equals-2.4.1
[Doppins] Upgrade dependency winston to 2.4.1
2018-03-22 17:59:17 +01:00
Martin Ek d2a6dad79a Merge pull request #370 from reactiflux/doppins/irc-colors-equals-1.4.2
[Doppins] Upgrade dependency irc-colors to 1.4.2
2018-03-22 17:59:04 +01:00
Doppins b4f6e50728 Upgrade dependency simple-markdown to 0.3.3 2018-03-11 01:13:56 +00:00
Doppins 85bce0caf6 Upgrade dependency winston to 2.4.1 2018-03-09 19:07:33 +00:00
Doppins 08e1b3dd68 Upgrade dependency irc-colors to 1.4.2 2018-02-26 04:14:26 +00:00
26 changed files with 6110 additions and 5044 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'
+65 -2
View File
@@ -1,6 +1,69 @@
# 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.
## [2.6.0] - 2018-03-22
### Added
* Support for posting messages to Discord using webhooks (thanks to
@@ -12,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
+36 -12
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,9 +38,23 @@ import config from './config.json';
discordIRC(config);
```
When installing the library, you may encounter an error relating to the installation of `iconv` or `node-icu-charset-detector`.
These are optional dependencies which allow you to set the target encoding of messages sent to Discord, as detailed below in the README.
Without these dependencies and the relevant setting, messages that aren't sent in UTF-8 may be corrupted when copied to Discord.
## 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).
@@ -73,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"
}
}
]
@@ -113,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
+95 -36
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,24 +279,23 @@ 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';
})
.replace(/<(:\w+:)\d+>/g, (match, emoteName) => emoteName);
.replace(/<a?(:\w+:)\d+>/g, (match, emoteName) => emoteName);
}
isCommandMessage(message) {
@@ -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.0",
"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.13.0",
"discord.js": "11.3.0",
"irc-colors": "1.4.1",
"commander": "^7.2.0",
"discord.js": "^12.5.3",
"irc-colors": "1.5.0",
"irc-formatting": "1.0.0-rc3",
"irc-upd": "0.8.1",
"lodash": "^4.17.4",
"simple-markdown": "0.3.2",
"strip-json-comments": "2.0.1",
"winston": "2.4.0"
"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();
});
+371 -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 = {
@@ -423,7 +472,16 @@ describe('Bot', function () {
this.bot.parseText(message).should.equal(':SCGWat:');
});
it('should convert user mentions from IRC', function () {
it('should convert animated emoji from discord', function () {
const message = {
mentions: { users: [] },
content: '<a:in_love:432887860270465028>'
};
this.bot.parseText(message).should.equal(':in_love:');
});
it('should convert user at-mentions from IRC', function () {
const testUser = this.addUser({ username: 'testuser', id: '123' });
const username = 'ircuser';
@@ -434,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`;
@@ -443,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' });
@@ -466,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: [] },
@@ -498,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,
@@ -512,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:'
]);
@@ -533,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: [] },
@@ -551,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);
});
@@ -687,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;
@@ -758,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';
@@ -770,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>';
@@ -782,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';
@@ -795,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: [] },
@@ -817,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 = {
@@ -842,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 = {
@@ -867,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 = {
@@ -907,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(
@@ -999,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() {}
};
}