Compare commits

..
197 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
Martin Ek 91a191ec37 2.6.0 2018-03-22 15:32:23 +01:00
Martin Ek 0d1dfd22f6 Update webhook instructions 2018-03-22 15:31:10 +01:00
Martin Ek c7263b72dd Merge pull request #230 from Fiaxhs/master
Use webhooks to reflect irc usernames + avatars
2018-03-22 15:22:30 +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
Martin Ek f660afda3b 2.5.1 2018-01-18 14:19:23 +01:00
Martin Ek b80d9f650d Upgrade and pin dependencies 2018-01-18 14:15:36 +01:00
Martin Ek 2da61b3e53 Merge pull request #340 from reactiflux/doppins/commander-equals-2.12.2
[Doppins] Upgrade dependency commander to 2.12.2
2018-01-18 04:48:05 -08:00
Fiaxhs 90c901a034 Merge remote-tracking branch 'upstream/master' 2018-01-11 11:31:48 +01:00
Edward Jones d2fe4200ed Update tests after irc-colors udpate to 1.4.1
irc-colors changed to use \x1d instead of \x16 for v1.4.1, which breaks
this test (erroneously); fix it by expecting the corrected character.

Motivation behind upstream change:
https://github.com/fent/irc-colors.js/issues/13
2017-12-27 22:27:09 +00:00
Edward Jones 0a13110c68 Update package.json, package-lock.json 2017-12-27 22:24:31 +00:00
Doppins aa00cfbce0 Upgrade dependency commander to 2.12.2 2017-11-28 07:09:09 +00:00
Martin Ek 68d0fd8f75 Merge pull request #339 from reactiflux/doppins/commander-equals-2.12.0
[Doppins] Upgrade dependency commander to 2.12.1
2017-11-23 12:32:21 +01:00
Doppins 8b62058541 Upgrade dependency commander to 2.12.1 2017-11-23 10:19:07 +00:00
Doppins 9ab541413f Upgrade dependency commander to 2.12.0 2017-11-22 23:05:09 +00:00
Martin Ek 7de32caa7a Merge pull request #333 from reactiflux/doppins/irc-upd-equals-0.8.1
[Doppins] Upgrade dependency irc-upd to 0.8.1
2017-11-16 22:12:03 +01:00
Doppins 93f1fc15d8 Upgrade dependency irc-upd to 0.8.1 2017-11-02 17:00:28 +00:00
Edward Jones 6e45493055 Fix typo in changelog (coveralls version) 2017-10-27 19:36:22 +01:00
Edward Jones 2e1fed8311 2.5.0 2017-10-27 19:23:54 +01:00
Edward Jones 88c50636b9 Update package-lock.json 2017-10-27 19:23:47 +01:00
Edward Jones 903eca635f Update changelog in preparation for 2.5.0 2017-10-27 19:20:03 +01:00
Martin Ek 4b2ffb2c2c Merge pull request #316 from fishyFrogFace/master
Make it work with group DM
2017-10-27 19:20:25 +02:00
Martin Ek a45e07c760 Merge pull request #273 from Throne3d/improve/mentions
Improve IRC → Discord mentions for non-word characters and partial word matches
2017-10-27 19:17:33 +02:00
Edward Jones cff6eee324 Fix linting in new test for long object 2017-10-23 01:07:02 +01:00
Edward Jones 0992dc2da1 Add tests for partial and overlapping matches 2017-10-23 01:07:02 +01:00
Edward Jones 188553b670 Improve IRC->Discord mentions for non-word characters and partial word matches 2017-10-23 01:07:02 +01:00
Martin Ek 569893adb6 Merge pull request #329 from Throne3d/test/node-8
Test against Node 8, update package-lock.json
2017-10-23 02:05:46 +02:00
Edward Jones 751b750776 Remove Node 7 from CI 2017-10-23 00:58:06 +01:00
Edward Jones 05573eedf4 Update package-lock.json
Some dependencies have changed version, and the package-lock.json file
hasn't been updated to reflect that. This rebuilds it to make it more in
line with the package.json file.
2017-10-23 00:53:56 +01:00
Edward Jones 3d2eeb2798 Test against Node 8
Node 8 has been out for a while, and should be supported as it seems
likely a lot of the userbase will be using it. Test it in Travis to make
sure there are no idiosyncracies between the versions.
2017-10-23 00:49:09 +01:00
Edward Jones db39ba237e Use a prepare script for transpiling instead of prepublish (#323)
From Travis logs:
As of npm@5, `prepublish` scripts will run only for `npm publish`.
(In npm@4 and previous versions, it also runs for `npm install`.)
See the deprecation note in `npm help scripts` for more information.

This was also causing discord-irc to fail to transpile when installed
using npm from source.

This modifies the script to run on the `prepare` hook, so it should
transpile when installed directly from source too.
2017-10-10 09:32:30 -07:00
Martin Ek 6eca392e00 Add missing commas 2017-10-10 09:31:57 -07:00
Martin Ek b86fd1eaa4 Merge remote-tracking branch 'origin/doppins/sinon-from-3.3.0-below-4.0.0' 2017-10-05 01:02:41 +02:00
Anirban Mukhopadhyay c3cac2b7ac Allow displayUsername to be used in command prelude (#321) 2017-10-05 01:01:47 +02:00
Martin Ek ab59a1ec66 Merge remote-tracking branch 'origin/doppins/babel-eslint-from-8.0.0-below-9.0.0' 2017-10-05 01:01:21 +02:00
Martin Ek dc27e8e137 Merge remote-tracking branch 'origin/doppins/discord.js-equals-11.2.0' 2017-10-05 01:00:38 +02:00
Martin Ek 0b266834f0 Set ircOptions.encoding in test configs 2017-10-05 00:59:27 +02:00
Martin Ek 87a3458bdd Upgrade eslint-config-airbnb 2017-10-05 00:54:01 +02:00
Doppins 1f49f74c85 Upgrade dependency winston to 2.4.0 (#318) 2017-10-05 00:35:18 +02:00
Doppins aa3d6fc665 Upgrade dependency mocha to ^4.0.0 (#319) 2017-10-05 00:35:12 +02:00
Martin Ek 3bd32206b4 Merge pull request #322 from anirbanmu/ignore-users-both-ways
Add the ability to ignore IRC/Discord users by nickname
2017-10-05 00:17:01 +02:00
Anirban Mukhopadhyay 94d90960c6 Respond to PR comments. Mostly cleanup. 2017-10-04 17:12:15 -05:00
Anirban Mukhopadhyay be281c5add Add ability to ignore discord users by nickname 2017-10-04 11:45:50 -05:00
Anirban Mukhopadhyay 0adcf50b02 Fix ESLint issue & add test for ignoring IRC users 2017-10-04 11:45:36 -05:00
Anirban Mukhopadhyay a12a186f8c Add ability to ignore certain IRC nicks 2017-10-04 11:45:11 -05:00
Doppins bc382d8d5c Upgrade dependency sinon to ^4.0.1 2017-10-04 08:58:09 +00:00
fishyFrogFace 78c4935614 Make it work with group DM 2017-09-30 23:12:17 +02:00
Edward Jones 7fbf1d0ba0 Set default encoding to UTF-8 (#315)
* Add a note about how to install optional charset converter dependencies

Fixes part of #252 and #237.

* Default ircOptions.encoding to utf-8 if node-irc can convert encodings

Also warn when started if the IRC library cannot convert between
encodings, in case users rely on this behavior.

* Improve config check and warning message around encodings
2017-09-30 16:35:59 +02:00
Doppins 1c0cc3fba4 Upgrade dependency coveralls to ^3.0.0 (#310) 2017-09-30 00:44:39 +02:00
Doppins 1cf6d644c8 [Doppins] Upgrade dependency simple-markdown to ^0.3.1 (#293)
* Upgrade dependency simple-markdown to ^0.3.0

* Upgrade dependency simple-markdown to ^0.3.1
2017-09-30 00:44:10 +02:00
Doppins 2fba3ee008 Upgrade dependency irc-upd to 0.8.0 (#313) 2017-09-30 00:43:26 +02:00
Doppins 59b9e071d5 Upgrade dependency babel-eslint to ^8.0.1 2017-09-27 20:53:30 +00:00
John M. Harris, Jr ddaa2e6325 Update README.md (#307)
If there's a link for Discord, there should be a link for IRC as well.
2017-09-27 01:51:42 +02:00
Doppins 69a8b8d9ae Upgrade dependency sinon to ^4.0.0 2017-09-26 07:58:57 +00:00
Doppins fdd7b1d200 Upgrade dependency sinon to ^3.3.0 2017-09-18 10:15:47 +00:00
Dan Hemberger 8bf36dd58d bot.js: enable autoRenick by default (#302)
When the client reconnects to IRC after a timeout, the bot nickname
is often still in use (the client receives an `err_nicknameinuse`
message), and the bot is given an alternate nickname by the server.
In this case, it is almost always desirable to try to change the
nickname back to the configured nickname, so we enable `autoRenick`
by default.

NOTE: `autoRenick` can be disabled by specifying `autoRenick: false`
in `ircOptions` in the discord-irc configuration file, or the
behavior can be modified with the `renickCount` and `renickDelay`
options. See the node-irc documentation for more details.
2017-09-18 06:56:13 +02:00
Eric Schneider c0a4133d03 Closes #299 (#301)
* I think this should work?

* Added test for multi-character command prefixes

(accidentally pushed to wrong branch there but let's hope no one notices)

* Fixed bot test. That looks better.
2017-09-18 00:54:36 +02:00
Doppins f8f7fea3b6 Upgrade dependency babel-eslint to ^8.0.0 2017-09-12 13:46:39 +00:00
Doppins 55b9d48c52 Upgrade dependency discord.js to 11.2.1 2017-09-03 18:56:14 +00:00
Doppins eecb782dc5 Upgrade dependency discord.js to 11.2.0 2017-09-03 17:25:30 +00:00
Martin Ek 345da09fcf Merge pull request #289 from reactiflux/release/2.4.2
Release 2.4.2
2017-08-31 00:41:26 -07:00
Edward Jones 65bb6fa85e 2.4.2 2017-08-21 15:56:35 +01:00
Edward Jones 631b0485aa Update changelog in preparation for 2.4.2 2017-08-21 15:56:30 +01:00
Fiaxhs 549b8fb767 One pass for webhook options
Variables renaming
Improved code readability
2017-08-03 13:52:25 +02:00
Fiaxhs ff788083d1 Better tests
Preventing crash on empty nickname
2017-07-31 15:12:31 +02:00
Fiaxhs e8c96e2a44 Fixed username/nickname matching 2017-07-31 10:59:58 +02:00
Fiaxhs 8a23a91931 Fix avatar lookup function (and tests) 2017-07-28 11:54:02 +02:00
Fiaxhs 4facb56ebb Updated avatar tests with new guild/members stub 2017-07-28 11:40:20 +02:00
Fiaxhs 07662761b4 Merge remote-tracking branch 'upstream/master' 2017-07-28 11:25:46 +02:00
Fiaxhs 34cdd373af Avatar matching is now more accurate and case insensitive 2017-07-28 11:19:25 +02:00
Fiaxhs d62dcfb9e6 Merge remote-tracking branch 'upstream/master' 2017-07-04 10:29:03 +02:00
Fiaxhs 8c0c11a2b1 Added webhooks section to readme 2017-05-30 16:11:59 +02:00
Fiaxhs 2bf761b163 getDiscordAvatar refactoring
Added test
2017-05-30 14:37:57 +02:00
Fiaxhs fa9ef71372 Merge remote-tracking branch 'upstream/master' 2017-05-30 14:35:58 +02:00
Fiaxhs b4452dd487 remove useless brackets 2017-05-02 13:06:21 +02:00
Fiaxhs 2c05038c37 Reworked webhook flow
IRC formatting
2017-05-02 13:03:00 +02:00
Fiaxhs 9ea90a9d96 Merge remote-tracking branch 'upstream/master' 2017-05-02 12:42:29 +02:00
Fiaxhs 3ca43ae0f4 use discord.js 11.1.0 2017-05-02 12:03:26 +02:00
Fiaxhs 2f59cbe2ef Implemented webhooks with matching avatars 2017-02-24 16:43:37 +01:00
32 changed files with 6684 additions and 4213 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'
- '7'
+121 -3
View File
@@ -1,18 +1,136 @@
# Changelog
This project adheres to [Semantic Versioning](http://semver.org/).
## [2.4.2] - 2017-08-21
## [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
[Fiaxhs](https://github.com/reactiflux/discord-irc/pull/230)!).
Webhooks lets you override nicknames and avatars, so messages coming from IRC
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.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://discord.com/api/webhooks/id/token"
}
```
## [2.5.1] - 2018-01-18
### Fixed
* Upgraded dependencies.
## [2.5.0] - 2017-10-27
### Added
* Support multi-character command prefixes - [#301](https://github.com/reactiflux/discord-irc/pull/301)
* Enable auto-renicking by default, so the bot tries to get the target nickname after it fails - [#302](https://github.com/reactiflux/discord-irc/pull/302)
* Add the ability to ignore IRC/Discord users by nickname - [#322](https://github.com/reactiflux/discord-irc/pull/322)
### Fixed
* Improve IRC → Discord mentions around non-word characters and nickname prefix matches - [#273](https://github.com/reactiflux/discord-irc/pull/273)
* Default to UTF-8 encoding when bridging messages to prevent character corruption - [#315](https://github.com/reactiflux/discord-irc/pull/315)
* Fix a crash when using the bot in a group DM - [#316](https://github.com/reactiflux/discord-irc/pull/316)
* Use a `prepare` script for transpiling instead of `prepublish`, fixing `npm` installation direct from the GitHub repository - [#323](https://github.com/reactiflux/discord-irc/pull/323)
* Update dependencies:
- discord.js to 11.2.1
- sinon to ^4.0.1
- irc-upd to 0.8.0 - [#313](https://github.com/reactiflux/discord-irc/pull/313)
- simple-markdown to ^0.3.1
- coveralls to ^3.0.0
- mocha to ^4.0.0
- winston to 2.4.0
### Changed
* Add a link to the IRC spec in the README - [#307](https://github.com/reactiflux/discord-irc/pull/307)
* Drop testing for Node 7, add testing for Node 8 - [#329](https://github.com/reactiflux/discord-irc/pull/329)
## [2.4.2] - 2017-08-21
### Fixed
* Tests: Use globbing instead of `find` so tests work on Windows - [#279](https://github.com/reactiflux/discord-irc/pull/279)
### Changed
* Update dependency irc-upd to [0.7.0](https://github.com/Throne3d/node-irc/releases/tag/v0.7.0); adds optional auto-renick functionality - [#284](https://github.com/reactiflux/discord-irc/pull/284)
* Update dependency irc-upd to [0.7.0](https://github.com/Throne3d/node-irc/releases/tag/v0.7.0) - [#284](https://github.com/reactiflux/discord-irc/pull/284)
* Tests: Use Discord objects to simplify code - [#272](https://github.com/reactiflux/discord-irc/pull/272)
## [2.4.1] - 2017-07-16
### Added
* Falsy command preludes are no longer sent (previously would choose default prelude) - [#260](https://github.com/reactiflux/discord-irc/pull/260)
+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
+65 -6
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 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,23 +88,40 @@ 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
"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.
"discordIds": ["198528216523210752"] // Ignore specified Discord ids and do not send their messages to IRC.
},
// List of webhooks per channel
"webhooks": {
"#discord": "https://discord.com/api/webhooks/id/token"
}
}
]
```
@@ -94,6 +130,29 @@ The `ircOptions` object is passed directly to irc-upd ([available options](https
To retrieve a discord channel ID, write `\#channel` on the relevant server – it should produce something of the form `<#1234567890>`, which you can then use in the `channelMapping` config.
### Webhooks
Webhooks lets you override nicknames and avatars, so messages coming from IRC
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.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://discord.com/api/webhooks/id/token"
}
```
### Encodings
If you encounter trouble with some characters being corrupted from some clients (particularly umlauted characters, such as `ä` or `ö`), try installing the optional dependencies `iconv` and `node-icu-charset-detector`.
The bot will produce a warning when started if the IRC library is unable to convert between encodings.
Further information can be found in [the installation section of irc-upd](https://github.com/Throne3d/node-irc#character-set-detection).
## Tests
Run the tests with:
```bash
+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
+255 -42
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,9 +40,18 @@ 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;
this.webhookOptions = options.webhooks;
// Nicks to ignore
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
@@ -49,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) {
@@ -61,10 +78,15 @@ 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 = {};
this.channelMapping = {};
this.webhooks = {};
// Remove channel passwords from the mapping and lowercase IRC channel names
_.forOwn(options.channelMapping, (ircChan, discordChan) => {
@@ -79,6 +101,16 @@ class Bot {
logger.debug('Connecting to IRC and Discord');
this.discord.login(this.discordToken);
// Extract id and token from Webhook urls and connect.
_.forOwn(this.webhookOptions, (url, channel) => {
const [id, token] = url.split('/').slice(-2);
const client = new discord.WebhookClient(id, token);
this.webhooks[channel] = {
id,
client
};
});
const ircOptions = {
userName: this.nickname,
realName: this.nickname,
@@ -86,13 +118,31 @@ class Bot {
floodProtection: true,
floodProtectionDelay: 500,
retryCount: 10,
autoRenick: true,
// options specified in the configuration file override the above defaults
...this.ircOptions
};
// default encoding to UTF-8 so messages to Discord aren't corrupted
if (!Object.prototype.hasOwnProperty.call(ircOptions, 'encoding')) {
if (irc.canConvertEncoding()) {
ircOptions.encoding = 'utf-8';
} else {
logger.warn('Cannot convert message encoding; you may encounter corrupted characters with non-English text.\n' +
'For information on how to fix this, please see: https://github.com/Throne3d/node-irc#character-set-detection');
}
}
this.ircClient = new irc.Client(this.server, this.nickname, ircOptions);
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');
@@ -217,9 +267,11 @@ class Bot {
}
static getDiscordNicknameOnServer(user, guild) {
const userDetails = guild.members.get(user.id);
if (userDetails) {
return userDetails.nickname || user.username;
if (guild) {
const userDetails = guild.members.cache.get(user.id);
if (userDetails) {
return userDetails.nickname || user.username;
}
}
return user.username;
}
@@ -227,28 +279,39 @@ 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) {
return this.commandCharacters.indexOf(message[0]) !== -1;
return this.commandCharacters.some(prefix => message.startsWith(prefix));
}
ignoredIrcUser(user) {
return this.ignoreUsers.irc.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) {
@@ -256,9 +319,16 @@ class Bot {
}
sendToIRC(message) {
const author = message.author;
const { author } = message;
// Ignore messages sent by the bot itself:
if (author.id === this.discord.user.id) return;
if (author.id === this.discord.user.id ||
Object.keys(this.webhooks).some(channel => this.webhooks[channel].id === author.id)
) return;
// Do not send to IRC if this user is on the ignore list.
if (this.ignoredDiscordUser(author)) {
return;
}
const channelName = `#${message.channel.name}`;
const ircChannel = this.channelMapping[message.channel.id] ||
@@ -270,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 = {
@@ -321,13 +398,21 @@ 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('Tried to send a message to a channel the bot isn\'t in: ',
discordChannelName);
logger.info(
'Tried to send a message to a channel the bot isn\'t in: ',
discordChannelName
);
return null;
}
return discordChannel;
@@ -335,16 +420,72 @@ class Bot {
return null;
}
findWebhook(ircChannel) {
const discordChannelName = this.invertedMapping[ircChannel.toLowerCase()];
return discordChannelName && this.webhooks[discordChannelName];
}
getDiscordAvatar(nick, channel) {
const guildMembers = this.findDiscordChannel(channel).guild.members.cache;
const findByNicknameOrUsername = caseSensitive =>
(member) => {
if (caseSensitive) {
return member.user.username === nick || member.nickname === nick;
}
const nickLowerCase = nick.toLowerCase();
return member.user.username.toLowerCase() === nickLowerCase
|| (member.nickname && member.nickname.toLowerCase() === nickLowerCase);
};
// Try to find exact matching case
let users = guildMembers.filter(findByNicknameOrUsername(true));
// Now let's search case insensitive.
if (users.size === 0) {
users = guildMembers.filter(findByNicknameOrUsername(false));
}
// No matching user or more than one => default avatar
if (users && users.size === 1) {
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;
}
// compare two strings case-insensitively
// for discord mention matching
static caseComp(str1, str2) {
return str1.toUpperCase() === str2.toUpperCase();
}
// check if the first string starts with the second case-insensitively
// for discord mention matching
static caseStartsWith(str1, str2) {
return str1.toUpperCase().startsWith(str2.toUpperCase());
}
sendToDiscord(author, channel, text) {
const discordChannel = this.findDiscordChannel(channel);
if (!discordChannel) return;
// Do not send to Discord if this user is on the ignore list.
if (this.ignoredIrcUser(author)) {
return;
}
// Convert text formatting (bold, italics, underscore)
const withFormat = formatFromIRCToDiscord(text);
const patternMap = {
author,
nickname: author,
displayUsername: author,
text: withFormat,
discordChannel: `#${discordChannel.name}`,
ircChannel: channel
@@ -361,35 +502,107 @@ class Bot {
return;
}
const withMentions = withFormat.replace(/@[^\s]+\b/g, (match) => {
const search = match.substring(1);
const guild = discordChannel.guild;
const nickUser = guild.members.find('nickname', search);
if (nickUser) {
return nickUser;
}
const { guild } = discordChannel;
const withMentions = withFormat.replace(/@([^\s#]+)#(\d+)/g, (match, username, discriminator) => {
// @username#1234 => mention
// skips usernames including spaces for ease (they cannot include hashes)
// checks case insensitively as Discord does
const user = guild.members.cache.find(x =>
Bot.caseComp(x.user.username, username)
&& x.user.discriminator === discriminator);
if (user) return user;
const user = this.discord.users.find('username', search);
if (user) {
return user;
}
return match;
}).replace(/^([^@\s:,]+)[:,]|@([^\s]+)/g, (match, startRef, atRef) => {
const reference = startRef || atRef;
const role = guild.roles.find('name', search);
if (role && role.mentionable) {
return role;
}
// this preliminary stuff is ultimately unnecessary
// but might save time over later more complicated calculations
// @nickname => mention, case insensitively
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.cache.find(x => Bot.caseComp(x.user.username, reference));
if (user) return user;
// @role => mention, case insensitively
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.
// @nameextra => [mention]extra, case insensitively, as Discord does
// uses the longest match, and if there are two, whichever is a match by case
let matchLength = 0;
let bestMatch = null;
let caseMatched = false;
// check if a partial match is found in reference and if so update the match values
const checkMatch = function (matchString, matchValue) {
// if the matchString is longer than the current best and is a match
// or if it's the same length but it matches by case unlike the current match
// set the best match to this matchString and matchValue
if ((matchString.length > matchLength && Bot.caseStartsWith(reference, matchString))
|| (matchString.length === matchLength && !caseMatched
&& reference.startsWith(matchString))) {
matchLength = matchString.length;
bestMatch = matchValue;
caseMatched = reference.startsWith(matchString);
}
};
// check users by username and nickname
guild.members.cache.forEach((member) => {
checkMatch(member.user.username, member);
if (bestMatch === member || !member.nickname) return;
checkMatch(member.nickname, member);
});
// check mentionable roles by visible name
guild.roles.cache.forEach((member) => {
if (!member.mentionable) return;
checkMatch(member.name, member);
});
// if a partial match was found, return the match and the unmatched trailing characters
if (bestMatch) return bestMatch.toString() + reference.substring(matchLength);
return match;
}).replace(/:(\w+):/g, (match, ident) => {
const guild = discordChannel.guild;
const emoji = guild.emojis.find(x => x.name === ident && x.requiresColons);
if (emoji) {
return `<:${emoji.identifier}>`; // identifier = name + ":" + id
}
// :emoji: => mention, case sensitively
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);
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;
}
patternMap.withMentions = withMentions;
// Add bold formatting:
+6 -4
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';
@@ -26,14 +25,17 @@ function readJSONConfig(filePath) {
function run() {
program
.version(version)
.option('-c, --config <path>',
.option(
'-c, --config <path>',
'Sets the path to the config file, otherwise read from the env variable CONFIG_FILE.'
)
.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') ?
+11 -5
View File
@@ -3,12 +3,18 @@ import SimpleMarkdown from 'simple-markdown';
import colors from 'irc-colors';
function mdNodeToIRC(node) {
let content = node.content;
let { content } = node;
if (Array.isArray(content)) content = content.map(mdNodeToIRC).join('');
if (node.type === 'em') return colors.italic(content);
if (node.type === 'strong') return colors.bold(content);
if (node.type === 'u') return colors.underline(content);
return content;
switch (node.type) {
case 'em':
return colors.italic(content);
case 'strong':
return colors.bold(content);
case 'u':
return colors.underline(content);
default:
return content;
}
}
export function formatFromDiscordToIRC(text) {
-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;
+5383 -3984
View File
File diff suppressed because it is too large Load Diff
+31 -29
View File
@@ -1,6 +1,6 @@
{
"name": "discord-irc",
"version": "2.4.2",
"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",
@@ -25,10 +25,10 @@
"scripts": {
"start": "node dist/index.js",
"build": "babel lib --out-dir dist",
"prepublish": "npm run build",
"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.11.0",
"discord.js": "11.1.0",
"irc-upd": "0.7.0",
"irc-colors": "^1.3.2",
"irc-formatting": "^1.0.0-rc3",
"lodash": "^4.17.4",
"simple-markdown": "^0.2.1",
"strip-json-comments": "2.0.1",
"winston": "2.3.1"
"commander": "^7.2.0",
"discord.js": "^12.5.3",
"irc-colors": "1.5.0",
"irc-formatting": "1.0.0-rc3",
"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": "^7.0.0",
"babel-preset-node6": "^11.0.0",
"babel-preset-stage-0": "^6.5.0",
"chai": "^4.0.2",
"coveralls": "^2.11.14",
"eslint": "^4.1.1",
"eslint-config-airbnb-base": "^11.0.1",
"eslint-plugin-import": "^2.2.0",
"mocha": "^3.1.2",
"nyc": "^11.0.3",
"sinon": "^2.0.0",
"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"
}
}
+9 -5
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();
});
@@ -158,7 +160,9 @@ describe('Bot Events', function () {
bot.channelUsers.should.be.an('object');
const channel = '#channel';
// nick => '' means the user is not a special user
const nicks = { [bot.nickname]: '', user: '', user2: '@', user3: '+' };
const nicks = {
[bot.nickname]: '', user: '', user2: '@', user3: '+'
};
bot.ircClient.emit('names', channel, nicks);
const channelNicks = new Set([bot.nickname, 'user', 'user2', 'user3']);
bot.channelUsers.should.deep.equal({ '#channel': channelNicks });
+509 -99
View File
@@ -3,65 +3,70 @@ 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';
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.bot = new Bot(config);
this.bot.connect();
this.sendWebhookMessageStub = sandbox.stub();
discord.WebhookClient = createWebhookStub(this.sendWebhookMessageStub);
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;
};
});
@@ -96,17 +101,21 @@ describe('Bot', function () {
this.sendStub.should.have.been.calledWith(formatted);
});
it('should not send messages to discord if the channel isn\'t in the channel mapping',
it(
'should not send messages to discord if the channel isn\'t in the channel mapping',
function () {
this.bot.sendToDiscord('user', '#no-irc', 'message');
this.sendStub.should.not.have.been.called;
});
}
);
it('should not send messages to discord if it isn\'t in the channel',
it(
'should not send messages to discord if it isn\'t in the channel',
function () {
this.bot.sendToDiscord('user', '#otherirc', 'message');
this.sendStub.should.not.have.been.called;
});
}
);
it('should send to a discord channel ID appropriately', function () {
const username = 'testuser';
@@ -116,30 +125,35 @@ describe('Bot', function () {
this.sendStub.should.have.been.calledWith(formatted);
});
it('should not send special messages to discord if the channel isn\'t in the channel mapping',
it(
'should not send special messages to discord if the channel isn\'t in the channel mapping',
function () {
this.bot.sendExactToDiscord('#no-irc', 'message');
this.sendStub.should.not.have.been.called;
});
}
);
it('should not send special messages to discord if it isn\'t in the channel',
it(
'should not send special messages to discord if it isn\'t in the channel',
function () {
this.bot.sendExactToDiscord('#otherirc', 'message');
this.sendStub.should.not.have.been.called;
});
}
);
it('should send special messages to discord',
it(
'should send special messages to discord',
function () {
this.bot.sendExactToDiscord('#irc', 'message');
this.sendStub.should.have.been.calledWith('message');
this.debugSpy.should.have.been.calledWith('Sending special message to Discord', 'message', '#irc', '->', '#discord');
});
}
);
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: [] },
@@ -153,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 = {
@@ -265,8 +301,10 @@ describe('Bot', function () {
this.bot.sendToIRC(message);
ClientStub.prototype.say.should.have.been.calledWith('#irc',
`<\u000304${message.author.username}\u000f> ${text}`);
ClientStub.prototype.say.should.have.been.calledWith(
'#irc',
`<\u000304${message.author.username}\u000f> ${text}`
);
const expected = `<\u000304${message.author.username}\u000f> ${attachmentUrl}`;
ClientStub.prototype.say.should.have.been.calledWith('#irc', expected);
@@ -305,7 +343,8 @@ describe('Bot', function () {
ClientStub.prototype.say.should.not.have.been.called;
});
it('should not send messages to irc if the channel isn\'t in the channel mapping',
it(
'should not send messages to irc if the channel isn\'t in the channel mapping',
function () {
const message = {
channel: {
@@ -320,7 +359,34 @@ describe('Bot', function () {
this.bot.sendToIRC(message);
ClientStub.prototype.say.should.not.have.been.called;
});
}
);
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>';
@@ -406,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';
@@ -417,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`;
@@ -426,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' });
@@ -449,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: [] },
@@ -480,6 +610,29 @@ describe('Bot', function () {
ClientStub.prototype.say.getCall(1).args.should.deep.equal(['#irc', text]);
});
it('should support multi-character command prefixes', function () {
this.setCustomBot({ ...config, commandCharacters: ['@@'] });
const text = '@@test command';
const message = {
content: text,
mentions: { users: [] },
channel: {
name: 'discord'
},
author: {
username: 'test',
id: 'not bot id'
},
guild: this.guild
};
this.bot.sendToIRC(message);
ClientStub.prototype.say.getCall(0).args.should.deep.equal([
'#irc', 'Command sent from Discord by test:'
]);
ClientStub.prototype.say.getCall(1).args.should.deep.equal(['#irc', text]);
});
it('should hide usernames for commands to Discord', function () {
const username = 'ircuser';
const text = '!command';
@@ -492,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: [] },
@@ -510,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);
});
@@ -531,7 +683,24 @@ describe('Bot', function () {
const username = 'ircuser';
const text = 'Hello, @testuser!';
const expected = `**<${username}>** Hello, <@${testUser.id}>!`;
const expected = `**<${username}>** Hello, ${testUser}!`;
this.bot.sendToDiscord(username, '#irc', text);
this.sendStub.should.have.been.calledWith(expected);
});
it('should convert username-discriminator mentions from IRC properly', function () {
const user1 = this.addUser({ username: 'user', id: '123', discriminator: '9876' });
const user2 = this.addUser({
username: 'user',
id: '124',
discriminator: '5555',
nickname: 'secondUser'
});
const username = 'ircuser';
const text = 'hello @user#9876 and @user#5555 and @fakeuser#1234';
const expected = `**<${username}>** hello ${user1} and ${user2} and @fakeuser#1234`;
this.bot.sendToDiscord(username, '#irc', text);
this.sendStub.should.have.been.calledWith(expected);
@@ -599,56 +768,37 @@ describe('Bot', function () {
this.sendStub.should.have.been.calledWith(expected);
});
it('should successfully send messages with default config', function () {
const bot = new Bot(configMsgFormatDefault);
bot.connect();
it('should convert overlapping mentions from IRC properly and case-insensitively', function () {
const user = this.addUser({ username: 'user', id: '111' });
const nickUser = this.addUser({ username: 'user2', id: '112', nickname: 'userTest' });
const nickUserCase = this.addUser({ username: 'user3', id: '113', nickname: 'userTEST' });
const role = this.addRole({ name: 'userTestRole', id: '12345', mentionable: true });
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
};
const username = 'ircuser';
const text = 'hello @User, @user, @userTest, @userTEST, @userTestRole and @usertestrole';
const expected = `**<${username}>** hello ${user}, ${user}, ${nickUser}, ${nickUserCase}, ${role} and ${role}`;
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.bot.sendToDiscord(username, '#irc', text);
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();
it('should convert partial matches from IRC properly', function () {
const user = this.addUser({ username: 'user', id: '111' });
const longUser = this.addUser({ username: 'user-punc', id: '112' });
const nickUser = this.addUser({ username: 'user2', id: '113', nickname: 'nick' });
const nickUserCase = this.addUser({ username: 'user3', id: '114', nickname: 'NiCK' });
const role = this.addRole({ name: 'role', id: '12345', mentionable: true });
const username = 'test';
const msg = 'test @user <#1234>';
const expected = `<test> #irc => #discord: ${msg}`;
bot.sendToDiscord(username, '#irc', msg);
const username = 'ircuser';
const text = '@user-ific @usermore, @user\'s friend @user-punc, @nicks and @NiCKs @roles';
const expected = `**<${username}>** ${user}-ific ${user}more, ${user}'s friend ${longUser}, ${nickUser}s and ${nickUserCase}s ${role}s`;
this.bot.sendToDiscord(username, '#irc', text);
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;
@@ -671,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';
@@ -683,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>';
@@ -695,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';
@@ -708,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: [] },
@@ -730,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 = {
@@ -755,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 = {
@@ -780,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 = {
@@ -807,4 +994,227 @@ describe('Bot', function () {
this.sendStub.should.have.been.calledOnce;
this.sendStub.getCall(0).args.should.deep.equal([msg]);
});
it('should create webhooks clients for each webhook url in the config', function () {
this.bot.webhooks.should.have.property('#withwebhook');
});
it('should extract id and token from webhook urls', function () {
this.bot.webhooks['#withwebhook'].id.should.equal('id');
});
it('should find the matching webhook when it exists', function () {
this.bot.findWebhook('#ircwebhook').should.not.equal(null);
});
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 prefer webhooks to send a message', function () {
this.bot.sendToDiscord('nick', '#irc', 'text');
this.sendWebhookMessageStub.should.have.been.called;
});
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('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('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('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(
'should not send messages to Discord if IRC user is ignored',
function () {
this.bot.sendToDiscord('irc_ignored_user', '#irc', 'message');
this.sendStub.should.not.have.been.called;
}
);
it(
'should not send messages to IRC if Discord user is ignored',
function () {
const message = {
content: 'text',
mentions: { users: [] },
channel: {
name: 'discord'
},
author: {
username: 'discord_ignored_user',
id: 'some id'
},
guild: this.guild
};
this.bot.sendToIRC(message);
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
});
+4 -1
View File
@@ -3,7 +3,10 @@
"server": "irc.freenode.net",
"discordToken": "whatapassword",
"commandCharacters": ["!", "."],
"ircOptions": {
"encoding": "utf-8"
},
"channelMapping": {
"#discord": "#irc"
}
}
}
+13 -1
View File
@@ -3,6 +3,9 @@
"server": "irc.freenode.net",
"discordToken": "whatapassword",
"commandCharacters": ["!", "."],
"ircOptions": {
"encoding": "utf-8"
},
"autoSendCommands": [
["MODE", "test", "+x"],
["AUTH", "test", "password"]
@@ -10,6 +13,15 @@
"channelMapping": {
"#discord": "#irc channelKey",
"#notinchannel": "#otherIRC",
"1234": "#channelforid"
"1234": "#channelforid",
"#withwebhook": "#ircwebhook"
},
"webhooks": {
"#withwebhook": "https://discord.com/api/webhooks/id/token"
},
"ignoreUsers": {
"irc": ["irc_ignored_user"],
"discord": ["discord_ignored_user"],
"discordIds": ["4499"]
}
}
+6
View File
@@ -3,6 +3,9 @@
"nickname": "test",
"server": "irc.freenode.net",
"discordToken": /* comment */ "whatapassword",
"ircOptions": {
"encoding": "utf-8"
},
"channelMapping": {
"#discord": "#irc" // Comment
}
@@ -12,6 +15,9 @@
"nickname": "test2",
"server": "irc.freenode.net",
"discordToken": "whatapassword",
"ircOptions": {
"encoding": "utf-8"
},
"channelMapping": {
"#discord": "#irc"
}
+6
View File
@@ -3,6 +3,9 @@
"nickname": "test",
"server": "irc.freenode.net",
"discordToken": "whatapassword",
"ircOptions": {
"encoding": "utf-8"
},
"channelMapping": {
"#discord": "#irc"
}
@@ -11,6 +14,9 @@
"nickname": "test2",
"server": "irc.freenode.net",
"discordToken": "whatapassword",
"ircOptions": {
"encoding": "utf-8"
},
"channelMapping": {
"#discord": "#irc"
}
+6
View File
@@ -3,6 +3,9 @@ module.exports = [
nickname: 'test',
server: 'irc.freenode.net',
discordToken: 'whatapassword',
ircOptions: {
encoding: 'utf-8'
},
channelMapping: {
'#discord': '#irc'
}
@@ -11,6 +14,9 @@ module.exports = [
nickname: 'test2',
server: 'irc.freenode.net',
discordToken: 'whatapassword',
ircOptions: {
encoding: 'utf-8'
},
channelMapping: {
'#discord': '#irc'
}
+3 -3
View File
@@ -12,8 +12,8 @@ describe('Formatting', () => {
});
it('should convert italic markdown', () => {
formatFromDiscordToIRC('*text*').should.equal('\x16text\x16');
formatFromDiscordToIRC('_text_').should.equal('\x16text\x16');
formatFromDiscordToIRC('*text*').should.equal('\x1dtext\x1d');
formatFromDiscordToIRC('_text_').should.equal('\x1dtext\x1d');
});
it('should convert underline markdown', () => {
@@ -26,7 +26,7 @@ describe('Formatting', () => {
it('should convert nested markdown', () => {
formatFromDiscordToIRC('**bold *italics***')
.should.equal('\x02bold \x16italics\x16\x02');
.should.equal('\x02bold \x1ditalics\x1d\x02');
});
});
+60 -13
View File
@@ -3,28 +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() {
+4 -1
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];
this.nick = args[1]; // eslint-disable-line prefer-destructuring
}
disconnect() {}
}
export default ClientStub;
+16
View File
@@ -0,0 +1,16 @@
/* eslint-disable class-methods-use-this */
export default function createWebhookStub(sendWebhookMessage) {
return class WebhookStub {
constructor(id, token) {
this.id = id;
this.token = token;
}
send(...args) {
sendWebhookMessage(...args);
return new Promise(() => {});
}
destroy() {}
};
}