Compare commits

..

1034 Commits

Author SHA1 Message Date
20582defd5
Enclosing DEBUG statements in dbot.config.debugMode
Signed-off-by: Georg <georg@lysergic.dev>
2021-08-29 20:24:53 +02:00
ac3d2761bc
PASS authentication and fixed PING-PONG
Signed-off-by: Georg <georg@lysergic.dev>
2021-08-29 20:03:49 +02:00
70a21d2b18
Signed-off-by: Georg <georg@lysergic.dev>
Init

- Initial fixes in modules
- Bundled adapted jsbot
- Elaborative README.md
2021-08-24 21:16:26 +02:00
reality
77a9c346d8
Merge pull request #688 from powerjungle/master
Add new clear tells command
2021-03-15 13:53:05 +00:00
powerjungle
97ebd371bf
Add new clear tells command 2021-03-10 15:05:39 +01:00
reality
ec3d6ed13b
Merge pull request #687 from powerjungle/master
Add README.md to tell module
2021-02-28 14:24:15 +00:00
powerjungle
a245d385f7
Add README.md to tell module 2021-02-28 13:51:47 +01:00
Luke Slater
695398d8fb
Merge pull request #682 from Scritches/master
Initial commit of new todo list module
2019-04-24 22:46:53 +01:00
Scritches
08922c8e67 Initial commit of new todo list module 2018-11-02 16:21:51 -04:00
Luke Slater
dfdb31b31d
Merge pull request #680 from Scritches/master
Improved error messages for LastFM module.
2018-09-02 11:54:02 +01:00
Scritches
1b18c9f048 Improved error messages for LastFM module. 2018-09-01 22:21:55 -04:00
Luke Slater
8c97c665a6
Merge pull request #679 from Scritches/master
Urban dictionary API changed - removed result_type
2018-08-27 19:26:50 +01:00
Scritches
1657e7cc02 Urban dictionary API changed - removed result_type 2018-08-26 18:08:01 -04:00
Luke Slater
09f8f525e6
time is literally the stupidest thing there is 2018-06-06 23:20:39 +01:00
reality
d64375ca50 sasl hint 2018-05-28 14:35:11 +00:00
reality
a4697e8d36 Merge branch 'master' of https://github.com/reality/dbot 2018-05-20 21:46:26 +00:00
reality
a97bda3c20 HA 2018-05-20 21:46:19 +00:00
Luke Slater
ab6cb80ea2
Merge pull request #677 from Scritches/master
'kick' and 'kill_namespam' module changes
2018-05-16 10:34:38 +01:00
Scritches
c26be5410f
Merge branch 'master' into master 2018-05-15 20:12:40 -04:00
Scritches
e11abe5af4 kills are deferred until user joins a non-exempt channel 2018-05-15 20:09:43 -04:00
Luke Slater
6a255244df
fix save callback lack (lol) 2018-05-12 18:56:14 +01:00
Scritches
f3dcc0e693 ~nban no longer adds redundant +b if user was killed 2018-05-05 13:45:54 -04:00
Scritches
ed69582c7a 4-indent to 2-indent 2018-05-05 13:35:49 -04:00
Luke Slater
814a84ee8e
Merge pull request #676 from Scritches/master
Various module fixes / improvements
2018-05-05 16:34:04 +01:00
Scritches
a5f9998872 Added ~sp as alias to ~spotify 2018-05-01 16:58:52 -04:00
Scritches
d544dcf017 clikill spares SASL-authenticated users from match kills 2018-05-01 16:47:25 -04:00
Scritches
bbfc4c18a6 xkcd title / relevent text search feature done right. 2018-04-26 19:12:20 -04:00
Scritches
0c8c377b08 ~xkcd command now finds relevant XKCDs when given text instead of a number 2018-04-26 17:14:18 -04:00
Scritches
c298813b28 Adds OMDB module to replace the RottenTomatoes module.
~movie command replaces ~rt command. API key from http://www.omdbapi.com/apikey.aspx
2018-04-26 16:58:15 -04:00
Scritches
0f0877f27a Fixed YouTube video duration reporting 2018-04-26 16:50:21 -04:00
reality
554a9e995b do not fail broken regex 2018-04-20 21:01:11 +00:00
Luke Slater
ae989d60ae
Merge pull request #675 from Scritches/master
Adds a module that queries the Online Encyclopedia of Integer Sequences
2018-04-19 21:50:58 +01:00
Scritches
f7b83e98b7 support for random sequences 2018-04-18 00:50:22 -04:00
Scritches
b415ee086f Add OEIS module
## Queries the **Online Encyclopedia of Integer Sequences** for interesting number sequences.

#### Sequence by example:
```
~sequence 1 2 3 4 5
```

#### Sequence by ID number
```
~sequence a27
```

#### Sequence by keywords
```
~sequence positive integers
```

In all cases the following is returned:
```
[oeis] A000027: "The positive integers." - (1, 2, 3, 4, 5, 6, 7, 8, ...) https://oeis.org/A000027
```
2018-04-18 00:26:05 -04:00
Luke Slater
944827c4d9
Merge pull request #674 from Scritches/master
Bunch of little fixes and style updates
2018-04-14 20:14:57 +01:00
Scritches
97642f35a9 Got rid of all the const
But muh self-documenting code :'(
2018-04-14 15:07:26 -04:00
Scritches
2d20f19572 Fix for lingering votequiets 2018-04-14 14:54:23 -04:00
Luke Slater
98c8894f95
Merge pull request #673 from Scritches/master
Initial rewrite of goodreads module using async/await
2018-04-14 19:51:57 +01:00
Scritches
11a7f1d20e Fix ~lastfm registered date display 2018-04-14 14:01:36 -04:00
Scritches
86d0a6f31b Link module title regexp fix
Fix for sites that use <\/title> instead of </title> to close their title elements.
2018-04-14 13:38:28 -04:00
Scritches
71c2c52d47 Initial rewrite of goodreads module using async/await
Covers original functionality plus adds a new ~reading command.

There is probably a lot of duplication in the module itself that can be cleaned up with some additional metaprogramming but *eh* I'm tired tonight.
2018-04-12 01:11:15 -04:00
Luke Slater
fbc4ac5afe
Merge pull request #672 from Scritches/master
Some more music module fixes
2018-04-11 14:46:23 +01:00
Scritches
a574b7d2ed more reasonable ~define
Tell wordnik to use the canonical version of the word where available (to avoid responses like "the plural of 'cat'" when doing "~define cats" - just tell me what cats are dammit)
2018-04-10 16:06:38 -04:00
Scritches
93ee3cb08b lastfm and spotify fixes
### lastfm module

removed ~taste and taste compare functions from lastfm - their API no longer supports these calls

Improved error reporting for ~suggestion and also parallelized the youtube/spotify queries

### spotify module

removed link matching - the Link module provides the exact same information as directly querying spotify so there's no real point
2018-04-10 15:35:57 -04:00
Scritches
597171c37d set last link in channel when ~yt command is used 2018-04-10 13:30:20 -04:00
Luke Slater
8182980420
Merge pull request #671 from Scritches/master
Words module api support for phrases
2018-03-30 17:24:30 +01:00
Scritches
bb28cebde1 Words module api support for phrases
~define, ~example, and ~like now all support phrases.
2018-03-30 12:22:03 -04:00
Luke Slater
48728c5801
Merge pull request #670 from Scritches/master
New minify module
2018-03-28 21:52:55 +01:00
Scritches
43cfe3b637 Minify module
basic framework for more human-readable error responses from the bitly minifier
2018-03-26 16:23:03 -04:00
Scritches
19cc4c474d New minify module
Added a module that provides URL minifier support for dbot.

Updated spotify and lastfm modules with support for url minimization via the include 'bitly' minimizer (which minimizes spotify.com links to spoti.fi - very nice)
2018-03-26 13:52:06 -04:00
Luke Slater
f5c9a275a9
Merge pull request #669 from Scritches/master
Bunch of spotify, youtube, and lastfm improvements
2018-03-25 03:28:22 +01:00
Scritches
ff7be1f31d https instead of http for ~listening youtube link 2018-03-24 22:10:57 -04:00
Scritches
0118f962b6 Include spotify desktop URI in ~syt results 2018-03-24 21:11:46 -04:00
Scritches
167525b5b9 Added ~ytpl command to search youtube for playlists 2018-03-24 20:57:57 -04:00
Scritches
25d572e8cb lastfm ~listening: make youtube and spotify api calls in parallel 2018-03-24 19:24:57 -04:00
Scritches
72a4552742 Revert "test"
This reverts commit bd148915ed.
2018-03-24 17:22:22 -04:00
Scritches
bd148915ed test 2018-03-24 17:21:26 -04:00
Scritches
354c0935b9 Move spotify api key into config; Restore spotify functionality to lastfm module ~listening command 2018-03-24 16:20:33 -04:00
Luke Slater
4a4e41b702
Merge pull request #668 from Scritches/master
Added padding around the urls in the output
2018-03-24 01:42:18 +00:00
Scritches
b87175e665 Added padding around the urls in the output 2018-03-23 21:37:43 -04:00
Luke Slater
9a7e1ab4bf
Merge pull request #666 from Scritches/master
fixed tabs/spaces and added spotify uri for desktop app linkability
2018-03-22 11:03:55 +00:00
Scritches
10c642c94f fixed tabs/spaces and added spotify uri for desktop app linkability 2018-03-22 03:04:14 -04:00
Luke Slater
dde0200032
Merge pull request #665 from Scritches/master
Added support for the minified youtube links returned by the ~yt command to the ~syt command
2018-03-08 21:25:03 +00:00
Scritches
217e65df96 Added necessary authentication for spotify API calls and fixed youtube link regex. 2018-03-08 16:11:12 -05:00
Scritches
03067f97d6 Added support for the minified youtube links returned by the ~yt command 2018-03-07 17:30:35 -05:00
reality
00215f62f0 that should learn it 2018-03-01 14:21:09 +00:00
reality
9a4a86c6ba weird config 2018-03-01 14:15:35 +00:00
reality
7d31560458 clean 2018-03-01 14:01:00 +00:00
reality
f55f645a3b some smal fixes and clarities 2018-03-01 13:57:45 +00:00
reality
7debad9a1d oopsie 2018-03-01 13:53:02 +00:00
reality
9faffa0d11 add cliconn autorek 2018-03-01 13:41:59 +00:00
reality
3c22b811ad clean up the bug with fresh whowas lookup 2018-02-28 17:42:08 +00:00
reality
329836d577 Merge branch 'master' of https://github.com/reality/dbot 2018-02-28 17:39:31 +00:00
reality
5796308e62 fix temp unban 2018-02-28 17:39:21 +00:00
Luke Slater
3972b5272b
the start of yo callback ain't a valid host 2018-02-24 00:56:26 +00:00
reality
17ecf62da0 finish alsuti 2018-02-23 14:03:16 +00:00
reality
9b55509257 alsuti sustatus 2018-02-23 13:47:23 +00:00
reality
69c06537e2 sustatus alsuti 2018-02-23 12:30:18 +00:00
reality
8fe032579d prevent multiple yes votes by using votequiet fallback (lol) 2018-02-22 18:28:10 +00:00
reality
fe8168a237 whowas works correctly as fallback for host lookup 2018-02-22 17:45:04 +00:00
reality
016c132158 start fix whowas 2018-02-22 17:30:15 +00:00
reality
9df86cdd99 ability to pass host manually to nunban, and extra info about host in notifies 2018-02-22 16:01:40 +00:00
reality
00ac08a9da autoquiet at 4 on votequiet, increase period to 1.5 mins 2018-02-17 18:41:04 +00:00
reality
eba4a9a4a1 votequiet on already polled user adds yes vote 2018-02-17 18:06:30 +00:00
reality
beaa420015 i am not very smart 2018-02-16 14:36:56 +00:00
reality
709cd085a9 add content kills 2018-02-16 14:29:17 +00:00
reality
56d12bd649 also add check for distinctive spam content 2018-02-16 14:22:16 +00:00
reality
510a02d08a exempt 2018-02-16 13:44:47 +00:00
reality
d7f212ea52 haha check out the bug there, that is actually really funny 2018-02-16 13:37:26 +00:00
reality
3b6dd18611 more syntax 2018-02-16 13:30:56 +00:00
reality
58256a8f62 appleg 2018-02-16 13:29:04 +00:00
reality
0da6af229f syntax! 2018-02-16 13:19:12 +00:00
reality
8310dc38ff module to deal with ppl hilighting lots of ppl at once. bad 2018-02-16 13:17:52 +00:00
reality
ebb0a023c0 more appropriate note string 2018-02-10 12:59:02 +00:00
reality
f097685c3d notes fixes 2018-02-10 12:47:54 +00:00
reality
4f269a5c02 wtf was that there for? 2018-02-10 12:40:27 +00:00
Luke Slater
777d738394 new notes functionality 2018-02-10 12:35:31 +00:00
reality
e62a1c8484 external lpmcount 2017-11-02 18:22:05 +00:00
krn
0f1c6c6782 Merge pull request #664 from somehibs/master
install script uses command -v for executable detection
2017-07-31 18:17:01 +01:00
alex
b8bbaab9d9 install script uses command -v for executable detection 2017-07-22 03:56:17 +01:00
reality
3204c422bb fixes 2017-07-18 19:03:00 +00:00
reality
ec86b6e1a8 fix indentation and stuff 2017-07-18 14:56:01 +00:00
reality
07156b2eb3 this 2017-07-18 14:32:42 +00:00
reality
d37326c88e fix all ban 2017-07-18 14:28:42 +00:00
reality
1f0e13bd2a new jsbot 2017-07-18 13:19:23 +00:00
reality
0d233f69b7 fix for nick tracking wrt new jsbot 2017-07-18 03:11:18 +00:00
reality
e22133d685 fix ignore 2017-07-18 02:52:59 +00:00
reality
0d2d00635c fixes 2017-07-18 02:51:45 +00:00
reality
d41836e937 index for aliases 2017-07-18 02:16:07 +00:00
reality
c1c54bbd59 fixes 2017-07-11 14:36:14 +00:00
reality
6a38eb5289 update with fixes 2017-07-11 14:22:18 +00:00
reality
d25ec221c7 fix for new jsbot 2017-07-11 14:21:59 +00:00
reality
54efa27ff2 update jsbot 2017-07-11 12:44:38 +00:00
reality
f02d2b74df curse you spotify 2017-06-05 17:29:12 +00:00
reality
330868686d oops 2017-03-25 00:43:31 +00:00
reality
9a44c41fbd qsearch random quote 2017-03-25 00:40:52 +00:00
reality
92598b13c2 channel with log 2016-12-14 22:09:19 +00:00
reality
a57b123fed two space indent for log and karma, karma logging 2016-12-14 21:58:42 +00:00
reality
ef86954025 fix for new web api 2016-12-05 20:04:37 +00:00
reality
7964bb9e88 spotify link in listening out 2016-12-05 19:48:08 +00:00
rlty
238c112986 Merge pull request #658 from speeddefrost/master
Fix broken regex. Misc cleanup. Add some brackets.
2016-10-17 06:53:48 +00:00
speeddefrost
d4d101bc94 Fix broken regex. Misc cleanup. Add some brackets. 2016-10-16 23:43:16 -07:00
rlty
5191eca0a4 Merge pull request #654 from speeddefrost/master
Merge common time parameters for remind commands. - see code review for additional comments
2016-10-12 10:49:59 +00:00
speeddefrost
3f599ddfc2 Merge common time parameters for remind commands. 2016-10-04 05:49:38 -07:00
reality
0e95f8e42a cspeed 2016-06-21 08:22:49 +00:00
reality
485e08ff97 fix unban 2016-05-12 07:39:03 +00:00
reality
ed24043a55 fix ban 2016-05-12 07:38:18 +00:00
reality
e8f657de94 that should learn it 2016-05-07 16:29:18 +00:00
reality
aeea5a7e18 multiple udp clients 2016-05-07 16:04:10 +00:00
reality
2b043d018b remove console log 2016-04-24 00:16:53 +00:00
reality
45a27904bd changes 2016-04-24 00:14:54 +00:00
reality
cfbb71faa6 that should learn it? 2016-04-13 22:25:51 +00:00
reality
d9093e6da8 that should learn it? 2016-04-13 22:19:14 +00:00
reality
930607b4fe that should learn it? 2016-04-13 22:15:03 +00:00
reality
601d10b0de test reddit colour 2016-04-13 22:09:20 +00:00
reality
92c045d8c7 fix object object nonsense 2016-04-12 19:59:08 +00:00
reality
face69d31a underban 2016-04-11 22:01:59 +00:00
reality
45153a45d2 regex missing 2016-04-11 21:53:57 +00:00
reality
e767e43771 years 2016-04-11 21:51:45 +00:00
reality
2ebeea1ef4 test 2016-04-11 21:49:17 +00:00
reality
a58e1fcc4b new jsbot 2016-04-11 21:34:16 +00:00
reality
7f2878ce58 api function 2016-04-11 18:23:02 +00:00
reality
a5657a5a36 cached 2016-04-11 18:15:10 +00:00
reality
bab36d5af3 better geoip 2016-04-11 18:08:20 +00:00
reality
d19fc76088 new jsbot 2016-04-06 00:53:19 +00:00
reality
fc0fe6f5be fix 2016-03-31 22:00:55 +00:00
reality
45e44bc7f8 fix 2016-03-31 21:59:31 +00:00
reality
d442461076 no 2016-03-23 17:32:05 +00:00
reality
c47677a590 remove file 2016-03-23 15:43:47 +00:00
reality
f2c61c95b9 summary 2016-03-23 15:36:15 +00:00
reality
e120869bcd redirects 2016-03-22 23:03:46 +00:00
reality
a14d042be2 redirects 2016-03-22 22:56:29 +00:00
reality
bc114a10e6 redirects 2016-03-22 22:54:17 +00:00
reality
5e8ccb6bcc redirects 2016-03-22 22:47:21 +00:00
reality
b61c482a32 redirects 2016-03-22 22:30:06 +00:00
reality
ba2ad2649f working 2016-03-22 21:36:26 +00:00
reality
0473597b0a better ~lol 2016-03-22 21:31:59 +00:00
reality
04fb1cc67b add channel name 2016-03-20 17:51:48 +00:00
reality
72e6fca9e5 timeout 2016-03-20 17:48:07 +00:00
reality
71632f401b or is it that 2016-03-12 22:51:08 +00:00
reality
d31a95b3a3 wrong order for tetris scores 2016-03-12 22:30:37 +00:00
reality
fa431ac800 vitetris 2016-03-12 22:16:02 +00:00
reality
aed72f52dc fix flashy 2016-03-04 17:18:35 +00:00
reality
b9064302ba fix spaces 2016-03-03 17:41:17 +00:00
reality
40e20f06b0 ~w 2016-03-03 17:39:08 +00:00
reality
84f8e7a151 ~w 2016-03-03 17:36:21 +00:00
reality
86c570a8c8 ~wikipedia 2016-03-03 17:31:42 +00:00
reality
fd1a76d3bc translate module 2016-03-03 16:02:10 +00:00
reality
266f6b58f4 translate module 2016-03-03 15:55:47 +00:00
reality
212ae87e8b translate module 2016-03-03 15:52:29 +00:00
reality
6038443225 rookie mistake 2016-02-21 20:06:51 +00:00
rlty
8ce27053ce Merge pull request #652 from EddieCurtis/patch-3
Fixing url for m.links.alternate
2016-02-20 23:07:22 +00:00
Eddie Curtis
e2949094a2 Fixing url for m.links.alternate 2016-02-20 23:06:28 +00:00
reality
8095aea3d0 rookie mistake 2016-02-19 19:28:01 +00:00
reality
2f6dc6f25f rookie mistake 2016-02-19 19:24:01 +00:00
reality
d1b764d4ec rookie mistake 2016-02-19 19:18:43 +00:00
reality
badf17b0ec rookie mistake 2016-02-19 19:17:05 +00:00
reality
f4a203e416 rookie mistake 2016-02-19 19:15:06 +00:00
reality
d4c3edca0a ffs lol 2016-02-19 19:14:10 +00:00
reality
c26c776b48 oops 2016-02-19 19:13:32 +00:00
reality
c247f48fbe fff 2016-02-19 19:12:22 +00:00
reality
da8c297973 things 2016-02-19 18:59:22 +00:00
reality
8be0261295 things 2016-02-19 18:57:11 +00:00
reality
991411f28d thing 2016-02-19 18:55:43 +00:00
reality
c63c17d9d5 things 2016-02-16 17:39:10 +00:00
reality
4478f4e405 things 2016-02-16 17:37:09 +00:00
reality
660693281f things 2016-02-16 17:32:29 +00:00
reality
a4141288e5 batchstatus 2016-02-02 17:31:36 +00:00
reality
a107c81ad3 batchstatus 2016-02-02 15:03:57 +00:00
reality
ad448a51fa batchstatus 2016-02-02 15:00:34 +00:00
reality
affb89662f time thing 2016-01-29 22:33:58 +00:00
reality
2d2d00ccbf fix order 2016-01-29 22:19:35 +00:00
reality
da2ad191c9 fix ban count 2016-01-29 22:13:44 +00:00
reality
a52fdbcea5 i like colours 2016-01-29 18:05:12 +00:00
reality
c16f65c5b8 i like colours 2016-01-29 18:03:22 +00:00
reality
4afeb8ff38 sustatus thing 2016-01-29 17:58:04 +00:00
reality
431266136c sustatus thing 2016-01-29 17:56:38 +00:00
reality
e52699b211 sustatus thing 2016-01-29 17:55:03 +00:00
reality
9ca81e3094 fix regex 2016-01-28 18:41:44 +00:00
reality
c10b65ef3b various fixes to the reporting system and related stuff 2016-01-28 18:29:51 +00:00
reality
ac9488bad9 offlineReporting configurable 2016-01-28 17:36:19 +00:00
reality
3af2cbb678 remove url from warn 2016-01-26 16:57:23 +00:00
reality
8c06ec9dcf helpful output 2016-01-15 23:22:59 +00:00
reality
dc00730168 remind-like time periods for nban and quiet 2016-01-15 21:36:24 +00:00
reality
eb35db1074 oops 2015-12-09 20:11:33 +00:00
reality
890103e4ab oops 2015-12-09 19:00:39 +00:00
reality
eebf1d9680 tell 2015-12-09 18:53:32 +00:00
reality
44f46cfc75 yes 2015-11-17 18:52:49 +00:00
reality
5abb1ab166 who cares about floods 2015-10-27 19:38:10 +00:00
reality
92e4db287d oops 2015-10-27 19:36:51 +00:00
reality
a378ec7ab4 oops 2015-10-27 19:33:08 +00:00
reality
2ea263f997 oops 2015-10-27 19:30:32 +00:00
reality
d473d95e42 even quicker like 2015-10-27 19:26:58 +00:00
reality
339fb3ddfb ban from current channel first 2015-10-27 19:22:29 +00:00
reality
ef500e2d96 mesg 2015-10-20 18:59:55 +00:00
reality
04c411c6c1 mesg 2015-10-20 18:58:04 +00:00
reality
04d797c6d3 mesg 2015-10-20 18:56:00 +00:00
reality
b49b9b99c4 mesg 2015-10-20 18:30:15 +00:00
reality
e88f2a70b1 mesg 2015-10-20 18:29:05 +00:00
reality
2d0fa7fe59 name clash 2015-10-20 18:26:39 +00:00
reality
5b7c52a63f oops 2015-10-20 18:24:24 +00:00
reality
74377c1db7 dnsbl 2015-10-20 18:22:40 +00:00
reality
939797f5be ignore pms for stats 2015-10-13 05:36:49 +00:00
reality
3a57f11f69 ~myreminders 2015-10-08 18:07:50 +00:00
reality
bece1b245f ~random moved to ~randomdata 2015-08-19 19:11:33 +00:00
reality
2e3d4582f1 derp 2015-08-13 21:03:21 +00:00
reality
0069003cc7 scope 2015-08-13 21:02:14 +00:00
reality
c6d352038c run ncount on other users 2015-08-13 20:59:56 +00:00
reality
25d26b5586 quote interpolation recursion 2015-08-13 14:36:22 +00:00
reality
11a0a76a9d auth external 2015-07-24 01:02:21 +00:00
reality
1252ef39d1 add target to record 2015-07-23 09:58:31 +00:00
reality
bf54039724 oops 2015-07-22 23:24:40 +00:00
reality
b82a3e72ff properly resolve unbans 2015-07-22 23:21:56 +00:00
reality
d5b35a7b13 change the input for wolfram alpha command 2015-07-21 13:40:02 +00:00
reality
deae0ae352 ye 2015-07-20 15:21:56 +00:00
reality
8cd0b07c76 ? 2015-07-20 15:19:57 +00:00
reality
db25eadedf only post a radio update every 2 minutes 2015-07-20 15:13:01 +00:00
reality
bfa4de5a1a filesize in imgur 2015-07-19 21:34:09 +00:00
reality
cf509cb3c5 that was dumb 2015-07-19 17:03:18 +00:00
reality
5f8cbe3fb1 only save the db to disk every minute 2015-07-19 16:56:08 +00:00
reality
36496b64ac fix predo for quit 2015-07-19 00:15:31 +00:00
reality
7ce6cb5dc2 import moment 2015-07-17 23:39:19 +00:00
reality
5084722e0b import moment 2015-07-17 23:38:17 +00:00
reality
a3fd5e71f3 record daily last used [#650] 2015-07-17 23:37:24 +00:00
reality
fd0d3829d8 add time to ~concerning 2015-07-17 22:52:35 +00:00
reality
7fcd277815 ~rmlastwarning 2015-07-08 20:47:15 +00:00
reality
050fa88e47 working flag 2015-07-07 18:17:10 +00:00
reality
24aaf298f7 fix another potential race condition 2015-07-07 17:58:56 +00:00
reality
024ee7913e fix race condition in getting flag stack 2015-07-07 17:45:12 +00:00
reality
f7785c95a3 oops 2015-06-25 14:30:14 +00:00
reality
c584e7b8f5 tagging at the back@ too 2015-06-25 14:28:21 +00:00
reality
2483ec1ef6 idk 2015-05-29 17:26:04 +00:00
reality
aba5ac4fa5 oops 2015-05-06 18:20:25 +00:00
reality
4895c12b6d fix for nowplaying 2015-05-06 18:18:27 +00:00
reality
b28a5f6655 fix for nowplaying 2015-05-06 18:17:06 +00:00
reality
b440a232fc [Close #639] 2015-05-03 09:29:36 +00:00
reality
a9dff5c39e merge nicks 2015-05-01 07:58:36 +00:00
reality
10f853c921 fix for below one minite 2015-04-27 09:51:45 +00:00
reality
b9786b1d2f fix for below one minite 2015-04-27 09:50:51 +00:00
reality
5bf1e24d02 fix for below one minite 2015-04-27 09:49:53 +00:00
reality
25347f9006 fix for below one minite 2015-04-27 09:32:22 +00:00
reality
a582600f0f fix for below one minite 2015-04-27 09:30:59 +00:00
reality
ea07d55b9e fix youtube stuff 2015-04-27 09:23:27 +00:00
reality
613b9cd3bd Merge branch 'master' of https://github.com/reality/dbot 2015-04-27 09:21:14 +00:00
reality
81cfa22dcf fix youtube stuff 2015-04-27 09:20:53 +00:00
Luke Slater
2b7077642d Merge pull request #649 from amki/master
Fixed CTCP module and added some new commands
2015-04-26 05:04:03 +03:00
amki
9e7153cf42 Fixed CTCP module and added some new commands 2015-04-26 02:07:38 +02:00
Luke Slater
06f6491d99 Merge pull request #648 from amki/crypto
Moved crypto module to internal node crypto api, remove ciphers, add random
2015-04-25 21:05:17 +03:00
reality
ac2a6036eb meh 2015-04-25 11:05:59 +00:00
reality
744e075697 meh 2015-04-25 11:05:03 +00:00
reality
c0e8290bba meh 2015-04-25 11:03:26 +00:00
reality
be71f31c07 meh 2015-04-25 11:00:51 +00:00
reality
f4d25c0bfa find alias notifies 2015-04-25 10:58:45 +00:00
reality
849067127f find alias notifies 2015-04-25 10:42:30 +00:00
reality
3d92550c76 meh 2015-04-25 10:36:21 +00:00
reality
cb2e4da2ae meh 2015-04-25 10:35:40 +00:00
reality
42952a24e5 meh 2015-04-25 10:34:34 +00:00
reality
9f685779d5 meh 2015-04-25 10:31:44 +00:00
reality
e271ac2885 meh 2015-04-25 10:30:42 +00:00
reality
0108de389b meh 2015-04-25 10:28:47 +00:00
reality
2820dfb0c7 more random recipes 2015-04-25 10:27:22 +00:00
reality
548d4977f2 order 2015-04-25 10:26:35 +00:00
amki
79dd53e4c4 Moved crypto module to internal node crypto api, removed (pretty) senseless cipher command and added random command 2015-04-25 05:48:19 +02:00
reality
15de4c4f5d more random recipes 2015-04-23 15:37:42 +00:00
reality
19442bfe8f typical 2015-04-23 15:18:28 +00:00
reality
8e31ac0ed6 typical 2015-04-23 15:17:38 +00:00
reality
6dd233d51f typical 2015-04-23 15:17:04 +00:00
reality
24c792e4cf oops 2015-04-23 15:13:02 +00:00
reality
9d682455f5 food suggest 2015-04-23 15:11:45 +00:00
reality
e46135d1ec lol 2015-04-23 15:00:56 +00:00
reality
80ebcb76fd hehe 2015-04-23 14:56:47 +00:00
reality
fcb32eaac4 hehe 2015-04-23 14:56:13 +00:00
reality
ff8dc8ce6d oops 2015-04-23 14:54:27 +00:00
reality
4467e9fd5b food 2015-04-23 14:53:33 +00:00
reality
0384a45bf7 hmm 2015-04-21 23:40:45 +00:00
reality
57a2a12814 fix seconds counter (thanks trapdoor) 2015-04-20 20:16:03 +00:00
reality
f6f6de508b change to new youtube api 2015-04-20 20:10:55 +00:00
Luke Slater
25b026cf26 Merge pull request #646 from EddieCurtis/patch-1
Moved period out of ban/kick reason
2015-04-19 15:49:41 +03:00
Eddie Curtis
91e6ef3612 Moved period out of ban/kick reason 2015-04-19 13:47:57 +01:00
Luke Slater
41c56be7da Merge pull request #642 from amki/rss
Updated RSS README to fix #630
2015-04-19 15:44:51 +03:00
Luke Slater
7376c380e7 Update pages.js 2015-04-14 01:50:37 +03:00
Luke Slater
8da13c35a7 Merge pull request #645 from amki/remind
Timers set through remind are now persistent
2015-04-07 00:39:42 +03:00
amki
fc06811907 Fixed bugs, removed debug output, timers now persistent 2015-04-06 23:38:21 +02:00
amki
063fb36567 still doesn't work 2015-04-06 21:25:33 +02:00
amki
7a138804a8 New iteration 2015-04-06 20:59:40 +02:00
amki
b54e5aa22d Started work on persistent timers 2015-04-06 20:46:21 +02:00
Luke Slater
0fdbc5fe68 Update kick.js 2015-04-06 00:51:06 +03:00
Luke Slater
37d5fca1af Merge pull request #644 from amki/remind
Remind now supports messages with spaces and calls the user reminding you
2015-04-05 20:50:07 +03:00
amki
7ac7817101 Remind now supports messages with spaces and calls the user reminding you 2015-04-05 19:48:47 +02:00
Luke Slater
448309dc37 Merge pull request #643 from amki/remind
Added remind module according to #171
2015-04-05 20:38:56 +03:00
amki
64bffd534e Added remind module with ~remind and ~remindme commands 2015-04-05 19:36:56 +02:00
Luke Slater
6b7ff075e4 Update rss.js
lol
2015-04-05 19:36:56 +02:00
amki
4cc8e26232 Updated RSS README to fix #630 2015-04-05 07:00:32 +02:00
Luke Slater
baaa444f42 Update rss.js
lol
2015-04-04 02:29:48 +03:00
Luke Slater
0da96ea9ff Merge pull request #641 from amki/rss
RSS module: URL shortener is now kinda optional + 2 bonus crashfixes
2015-04-04 02:27:55 +03:00
amki
db0c61b369 URL shortener is now optional, errors are being pasted to console 2015-04-04 01:24:50 +02:00
amki
1756ae84ef Fixed 2 crashes when rss database was empty 2015-04-04 00:28:36 +02:00
reality
65d752484f m is not n 2015-04-02 18:46:42 +00:00
reality
300a6c51a1 m is not n 2015-04-02 18:45:32 +00:00
reality
58ef784a1e m is not n 2015-04-02 18:44:37 +00:00
reality
c873d4888d blah 2015-04-02 18:41:38 +00:00
reality
8bdb11e9e1 remember your imports kids 2015-04-02 18:37:52 +00:00
reality
0536ce1efa tz 2015-04-02 18:35:40 +00:00
Luke Slater
6838e269b2 Update april.js 2015-04-01 00:43:06 +03:00
Luke Slater
db661c1229 Update april.js 2015-04-01 00:42:46 +03:00
reality
a07daddccc push 2015-03-31 18:51:53 +00:00
reality
e393fd6fdb ye 2015-03-31 18:50:44 +00:00
reality
3347b67cea optinal single quote 2015-03-31 18:48:57 +00:00
reality
999b6f44ad an support 2015-03-31 18:41:57 +00:00
reality
135a4b421e oops 2015-03-31 18:36:08 +00:00
reality
b7b57da78a shorter timeout and parse out a 2015-03-31 18:35:19 +00:00
reality
417cc7b5f8 set a small timeout 2015-03-31 18:32:18 +00:00
reality
33923aee8b OMG 2015-03-31 18:29:45 +00:00
reality
febddfcac7 update nban usage 2015-03-30 19:02:06 +00:00
reality
a27f789a37 get a random post from a given subreddit 2015-03-28 12:55:06 +00:00
reality
e57b0107f4 bleh 2015-03-24 19:57:49 +00:00
reality
dc2b61b21d bleh 2015-03-24 19:39:09 +00:00
reality
148a7a6693 interpol quote 2015-03-24 19:36:36 +00:00
reality
0a9a5a95d0 more likely 2015-03-24 19:34:12 +00:00
reality
bcdf86d7ff i am not wise 2015-03-24 19:33:06 +00:00
reality
16e9b2d9bd i am not wise 2015-03-24 19:32:19 +00:00
reality
902cbdec3c idk 2015-03-24 19:30:46 +00:00
reality
ac2f7bbccd fix regex examples 2015-03-24 19:26:41 +00:00
reality
33c50671e6 fix regex examples 2015-03-24 19:23:02 +00:00
reality
4d50f1eff2 random should response whatsit 2015-03-24 19:17:54 +00:00
reality
e892b0518a oops 2015-03-23 16:19:57 +00:00
reality
04cf918f7a oops 2015-03-23 16:17:32 +00:00
reality
17840f5c9c karma colour 2015-03-23 16:14:41 +00:00
reality
a2ea8cd260 extract err 2015-03-15 18:52:33 +00:00
reality
323be545ab extract err 2015-03-15 18:51:54 +00:00
reality
b41d01b4ad extract err 2015-03-15 18:49:32 +00:00
reality
22c9a5e011 extract err 2015-03-15 18:46:58 +00:00
reality
b54774994b in days 2015-03-14 14:00:56 +00:00
reality
028f56c289 in days 2015-03-14 13:59:45 +00:00
reality
bde2e5f231 colour and diff 2015-03-14 13:58:15 +00:00
reality
0dac6bd576 b& for 45 years 2015-03-14 13:50:31 +00:00
reality
ce149998d8 more trix 2015-03-14 13:49:30 +00:00
reality
8070875937 trix 2015-03-14 13:47:31 +00:00
reality
65d1c24adf ban thing 2015-03-14 13:45:15 +00:00
reality
148e4a43db oops 2015-03-14 10:17:19 +00:00
reality
46d5df86c2 yes 2015-03-14 10:16:17 +00:00
reality
36372a511c also glob name 2015-03-14 09:59:16 +00:00
reality
e523e35c73 today i have a serious case of the stupids. fix host var 2015-03-14 09:56:55 +00:00
reality
f3b1d0c4ef i actually am an idiot 2015-03-14 09:55:39 +00:00
reality
b8e3f13816 i actually am an idiot 2015-03-14 09:54:41 +00:00
reality
4aa2c05989 concerning 2015-03-14 09:52:51 +00:00
reality
96dc972637 concerning 2015-03-14 09:51:22 +00:00
reality
6fc93344a8 concerning 2015-03-14 09:49:15 +00:00
reality
89384492d1 concerning 2015-03-14 09:47:59 +00:00
reality
ad2448f2a0 concerning 2015-03-14 09:45:13 +00:00
reality
fb61ce21a5 concerning 2015-03-14 09:43:34 +00:00
reality
c779721d52 concerning 2015-03-14 09:42:13 +00:00
reality
840ed71188 store host in notify 2015-03-14 09:24:51 +00:00
Luke Slater
d83a2cab74 Merge pull request #638 from shymega/urlupd1
Updated d3.js again
2015-03-11 11:15:26 +03:00
reality
6c671b1ba7 oops 2015-03-09 13:35:38 +00:00
shymega
1d55c1ebce Updated d3.js again 2015-02-28 16:54:43 +00:00
reality
2f874686b6 fix security bug in imgur module 2015-02-27 09:49:46 +00:00
reality
3d8700a6d2 fix security bug in imgur module 2015-02-27 09:48:21 +00:00
reality
baeeee64d9 no karma from bot (quote abuse) 2015-02-19 17:14:27 +00:00
reality
cdc8cb0d74 limit length of karma thing 2015-02-19 17:12:25 +00:00
reality
cafe173911 Merge branch 'master' of github.com:reality/dbot 2015-02-18 16:12:54 +00:00
reality
d84b94f050 fixnomissingchan 2015-02-18 16:12:48 +00:00
Luke Slater
dda7beea10 Merge pull request #637 from Kagammor/patch-1
Don't show comma if no city is given
2015-02-17 12:13:06 +03:00
reality
77e67b637b oops 2015-02-17 08:49:16 +00:00
reality
c2573f9043 oops 2015-02-17 08:39:57 +00:00
Kagammor
c7733ee382 Don't show comma if no city is given
The OpenWeatherMap API may return weather for areas unlinked to a city, on which body.name will be undefined. This would previously show up as (for example) "[ , United Kingdom]". This simple ternary will only return a comma if it is also returning a city name.
2015-02-17 01:23:51 +01:00
reality
5aeeb77065 geoip 2015-02-15 18:26:25 +00:00
reality
e5ac647de4 oops 2015-02-13 00:17:38 +00:00
reality
2d1cfdd567 no more : 2015-02-11 22:24:53 +00:00
reality
81520a3aab fix indentation 2015-02-09 16:56:29 +00:00
reality
10c300ff2b only one connection to the database across all modules. master db driver in dbType of main config.json 2015-02-09 16:54:59 +00:00
reality
76a59dee55 #note #quiet warns 2015-02-08 21:10:23 +00:00
reality
6b7b5494f6 return fbman to ri results 2015-02-07 21:54:16 +00:00
reality
2f9133fcee return fbman to ri results 2015-02-07 21:53:27 +00:00
reality
e9e1e61540 ext quotecat 2015-02-06 12:40:15 +00:00
reality
fc81fc38a1 trim space from end of karma 2015-02-05 19:49:38 +00:00
reality
7749b2eb65 nope 2015-02-04 21:30:52 +00:00
reality
e8a18fa72f nope 2015-02-04 21:29:32 +00:00
reality
e831a7cfa5 /i 2015-02-04 18:52:35 +00:00
reality
11c303bcaa karma 2015-02-04 17:32:31 +00:00
reality
e426a306ab karma 2015-02-04 17:13:41 +00:00
reality
7ad9a5c6a0 karma 2015-02-04 17:07:18 +00:00
reality
931d2de4f1 karma 2015-02-04 17:06:00 +00:00
reality
4446825458 karma 2015-02-04 17:05:20 +00:00
reality
5cbb0362c0 karma 2015-02-04 17:04:50 +00:00
reality
6adc93f043 lc 2015-02-04 17:02:39 +00:00
reality
4a198e2f64 more karma 2015-02-04 17:00:47 +00:00
reality
164ec47337 fix thing +_+++++ 2015-02-04 16:38:56 +00:00
reality
d97e4969f9 karma 2015-02-04 16:34:59 +00:00
reality
720740de58 -b on thing 2015-01-30 00:07:45 +00:00
reality
b5bb0af0da idk 2015-01-29 23:59:55 +00:00
reality
1cbb57e519 inbuilt -b 2015-01-29 22:24:45 +00:00
reality
29bdb0e91e quietbans 2015-01-29 22:06:17 +00:00
reality
bf920874b1 derp 2015-01-29 21:47:18 +00:00
Luke Slater
eab1004215 Merge branch 'master' of https://github.com/reality/dbot 2015-01-29 21:42:18 +00:00
Luke Slater
57b6b1b7ee fixes 2015-01-29 21:42:09 +00:00
reality
c290b64285 full redir 2015-01-29 21:41:01 +00:00
reality
79a607ba1b weather country 2015-01-28 07:22:32 +00:00
reality
c87c4312ad weather better 2015-01-28 06:26:14 +00:00
reality
f440ffd405 Merge branch 'master' of github.com:reality/dbot 2015-01-28 06:16:25 +00:00
reality
1d10ed86e6 weather 2015-01-28 06:16:17 +00:00
Luke Slater
357fe04058 Merge pull request #635 from shymega/newinstall
Install script fix -URL's and config.json edit
2015-01-26 22:34:24 +00:00
shymega
501c8e66d9 Updated download URL's, and now edits config.json with $EDITOR variable,
or vim if the variable doesn't exist.
2015-01-24 18:19:40 +00:00
reality
ff342bc932 [Close #634] 2015-01-24 17:34:30 +00:00
reality
a101cfe437 domain err catch 2015-01-19 22:59:26 +00:00
reality
b3d7abbc3b debug 2015-01-19 22:33:28 +00:00
reality
4e4da78e0b debug 2015-01-19 22:31:18 +00:00
reality
c412ad0b23 revert 2015-01-19 22:30:04 +00:00
reality
52a5023ef7 asynx != async 2015-01-19 22:28:53 +00:00
reality
552b87c29e async output next 1s delay 2015-01-19 22:28:18 +00:00
reality
e5b8034e0f order by int not string 2015-01-19 22:25:03 +00:00
reality
3f6153a7cf order by int not string 2015-01-19 22:19:34 +00:00
reality
14f3a09c2c date formatting 2015-01-19 22:16:22 +00:00
reality
84e7b5f06e date formatting 2015-01-19 22:12:05 +00:00
reality
0e47ce22ce sort is one of the few functions afforded to the array object naturally 2015-01-19 22:08:54 +00:00
reality
4709a11fad time 2015-01-19 22:06:33 +00:00
reality
5b067094de reports 2015-01-19 21:57:27 +00:00
reality
439fcf567c ye 2015-01-15 02:46:02 +00:00
reality
97f44141b3 Revert "access"
This reverts commit 333c8de2ad.
2015-01-15 02:45:07 +00:00
reality
333c8de2ad access 2015-01-15 02:44:00 +00:00
reality
b1697464d9 sustatus 2015-01-15 02:41:11 +00:00
reality
4ca95aa1aa wordcrimes initial 2015-01-12 04:30:31 +00:00
reality
d3633fc21d new db without resolvchan 2015-01-09 21:25:33 +00:00
reality
5621bad05c currency 2015-01-09 01:43:20 +00:00
reality
d732440e3e yeah yeah 2015-01-09 01:11:47 +00:00
reality
b2188ea036 r2rio 2015-01-09 00:53:14 +00:00
reality
0a6f1bd82a gmaps 2015-01-08 18:06:20 +00:00
reality
d0db3b6a53 yes yes 2015-01-07 21:47:12 +00:00
reality
0d86dc4119 yes yes 2015-01-07 21:45:54 +00:00
reality
7e968d38bb period 2015-01-02 21:40:29 +00:00
reality
2c4aad8a23 whoops 2015-01-02 21:39:45 +00:00
reality
54f4d4979c types 2015-01-02 21:38:37 +00:00
reality
e2190444a8 ye 2014-12-24 00:19:25 +00:00
reality
2b31ce12d9 yeh 2014-12-16 02:50:03 +00:00
reality
36b41156a5 Merge branch 'master' of github.com:reality/dbot 2014-12-03 04:16:45 +00:00
reality
0f73bca593 yeh 2014-12-03 04:14:14 +00:00
Luke Slater
d647b35405 Update words.js 2014-11-30 04:01:14 +00:00
Luke Slater
047ed35f83 Update words.js 2014-11-30 03:59:19 +00:00
Luke Slater
72afc1ca0a Update words.js 2014-11-30 03:58:22 +00:00
Luke Slater
ba0d99b60d Update words.js 2014-11-30 03:57:41 +00:00
Luke Slater
f8cfc04ab2 Update words.js 2014-11-30 03:56:25 +00:00
reality
2d8a1da470 like 2014-11-29 05:10:34 +00:00
reality
0cf845f099 like 2014-11-29 05:08:26 +00:00
reality
f0398fe8fc like 2014-11-29 05:03:25 +00:00
reality
42b8b1f6ba like 2014-11-29 05:02:11 +00:00
reality
1a144c6459 etymology 2014-11-28 14:43:16 +00:00
reality
f7fbbc7bd0 and the strings... 2014-11-09 14:05:32 +00:00
reality
1b2f01af94 tell user about warn 2014-11-09 14:04:04 +00:00
Luke Slater
27c150f877 some ufixes 2014-11-09 13:59:00 +00:00
reality
bdbd5ee8e4 Words updates 2014-10-31 17:02:17 +00:00
reality
1b67b04630 like 2014-10-31 04:27:08 +00:00
reality
0c8d0c8143 like 2014-10-31 04:21:33 +00:00
reality
a45c578037 like 2014-10-31 04:19:05 +00:00
reality
d91c014b53 fix web import and bootstrap dl #629 2014-10-27 17:07:04 +00:00
Luke Slater
81b352c306 Merge pull request #627 from reality/betterusers
merge new users into master
2014-10-27 16:41:40 +00:00
Luke Slater
f309348e25 Update run.js 2014-10-22 03:10:24 +01:00
reality
977de65914 web changes for posting 2014-10-21 21:25:50 +00:00
Luke Slater
d7939a59cc changes 2014-10-21 15:08:55 +00:00
Luke Slater
cd92f0c4de updates 2014-10-21 15:08:26 +00:00
Luke Slater
c4828f7ef4 Update web.js 2014-10-19 23:55:45 +01:00
reality
9705f308fd fix wordusers 2014-10-16 16:08:58 +00:00
reality
a68108df07 remember to commit new files 2014-10-03 21:00:57 +00:00
reality
8a5089c0f6 t push:wq
:Merge branch 'betterusers' of github.com:reality/dbot into betterusers
2014-10-03 20:56:15 +00:00
reality
881f828f34 no strict match on chan 2014-10-03 20:56:11 +00:00
Luke Slater
5289efc72c blah 2014-10-03 20:54:30 +00:00
Luke Slater
9ed4c582e2 ffs 2014-10-03 20:45:51 +00:00
reality
a7ccb5ff14 akill 2014-10-03 20:42:41 +00:00
Luke Slater
80cd9ab9d6 Merge branch 'betterusers' of https://github.com/reality/depressionbot into betterusers 2014-10-01 00:29:03 +00:00
reality
d0aab89435 L:Merge branch 'betterusers' of github.com:reality/dbot into betterusers 2014-10-01 00:28:40 +00:00
reality
6d8224bdf1 leafly 2014-10-01 00:28:32 +00:00
Luke Slater
fb3ddf2da8 Update commands.js 2014-09-28 18:48:36 +01:00
Luke Slater
80d9af6279 Merge pull request #624 from amki/rss
Don't crash when rss feed item does not contain pubdate
2014-09-19 14:45:18 +01:00
amki
0a914c820a Don't crash when rss feed item does not contain pubdate 2014-09-18 21:07:29 +00:00
Luke Slater
038af50eaa Merge branch 'betterusers' of https://github.com/reality/depressionbot into betterusers 2014-09-18 03:33:45 +00:00
reality
9733fa6185 pls 2014-09-18 03:33:40 +00:00
Luke Slater
6895ad66f6 Merge branch 'betterusers' of https://github.com/reality/depressionbot into betterusers 2014-09-18 03:32:03 +00:00
reality
6b52141245 try again 2014-09-18 03:31:59 +00:00
Luke Slater
0eb29caf44 Merge branch 'betterusers' of https://github.com/reality/depressionbot into betterusers 2014-09-18 03:29:27 +00:00
reality
e1732ff26a blah 2014-09-18 03:29:20 +00:00
Luke Slater
f8bc0be366 whoops 2014-09-15 21:20:59 +00:00
Luke Slater
d184a251e4 whoops 2014-09-15 21:19:41 +00:00
reality
84ddfb90a6 repair nunsub 2014-09-15 21:12:30 +00:00
reality
83e5a35677 expand flags from hosts 2014-09-14 19:31:22 +00:00
reality
5560f96744 get users with given vhost 2014-09-14 19:02:45 +00:00
reality
2660995aee usage for crypto 2014-09-13 19:52:01 +00:00
reality
e3a6f53537 regex for usage 2014-09-13 19:48:28 +00:00
reality
2570eb99a6 remove tilde prefix for command usage 2014-09-13 19:40:59 +00:00
reality
0ebdb2dbde put udp bind in onload so it has this 2014-09-13 19:23:00 +00:00
reality
b660d9ce21 ok 2014-09-12 14:56:34 +00:00
reality
ac603c3b9c alignment is important mmkay? 2014-09-12 14:53:41 +00:00
reality
9a7827448d fix flashy 2014-09-12 14:52:45 +00:00
Luke Slater
a2a025ad55 Update install 2014-09-11 20:21:16 +01:00
reality
77d9997009 fix nickstealing 2014-09-09 09:52:45 +00:00
reality
402c8e80f4 missing err in readUser on reload ignore repopulation 2014-09-08 21:08:28 +00:00
reality
5551e8545f Merge branch 'betterusers' of github.com:reality/dbot into betterusers 2014-09-08 21:02:16 +00:00
reality
a496526049 fix ignore behaviour with command/module clash 2014-09-08 21:02:10 +00:00
Luke Slater
03bab4e7db some fixes 2014-09-08 10:42:59 +00:00
reality
63ded5b197 that should do it 2014-09-08 10:41:09 +00:00
reality
57742d13f2 fix flag regex 2014-09-06 18:43:26 +00:00
reality
1d9765c95b correct user ids 2014-09-06 12:01:32 +00:00
reality
64e7294233 final fixes 2014-09-05 13:51:51 +00:00
reality
15e3971f11 Merge branch 'betterusers' of github.com:reality/dbot into betterusers 2014-09-05 12:06:16 +00:00
reality
64cdd374bc fix profiles. also the fact it was getting all the users onLoad is probably a badthing 2014-09-05 12:06:10 +00:00
Luke Slater
8d9ef6365e Update atheme.js 2014-09-05 12:38:33 +01:00
reality
bea3283b05 no offline messages support for those using anything but my changes to atheme. gg -.- 2014-09-05 11:29:25 +00:00
reality
8c5e871640 report report (it works) 2014-09-05 10:42:30 +00:00
reality
7cb53a8b5a now it works 2014-09-05 09:15:05 +00:00
reality
24d14e7005 read flags 2014-09-05 08:51:48 +00:00
reality
e83b4af33f move charybdis module to atheme 2014-09-04 15:26:28 +00:00
reality
8dfe1a37c4 remove getchannel stuff 2014-09-04 15:25:21 +00:00
reality
ef250fbc05 fix resolveuser references 2014-09-04 14:54:49 +00:00
reality
e096daa605 fix getUser calls 2014-09-04 14:48:20 +00:00
reality
c519727b6c fix login 2014-09-04 14:41:57 +00:00
reality
78057dca3c update web to use new express version 2014-09-04 14:19:23 +00:00
reality
b18a61ebd1 fix reports and warning 2014-09-04 13:43:53 +00:00
reality
34acae025b fix most of the sstats stuff, still got to do merge trigger 2014-09-04 13:04:15 +00:00
reality
1037070bff fixes for merge and setaliasparent, basic sstats functionality 2014-09-04 12:15:11 +00:00
reality
8ba4db33a7 some bug fixes 2014-09-04 10:15:52 +00:00
reality
905ced9485 add merge users command and fix up strings 2014-09-04 09:51:39 +00:00
reality
0a4240f3db replace users with new modules 2014-09-04 09:36:57 +00:00
reality
be07c36294 tings 2014-09-04 09:30:22 +00:00
reality
b950426d7b well, there's a start 2014-09-04 08:27:46 +00:00
Luke Slater
1a590bd678 Merge pull request #623 from shymega/master
dbot - install script fix
2014-09-03 07:28:20 +01:00
Dominic Z. Rodriguez
b1a6a85ff7 Just a minor fix to a filename 2014-09-02 18:58:32 +01:00
Dominic Z. Rodriguez
87bc11061d Updated the shell script 'install' for dbot, download urls for bootstrap and d3js were not working. 2014-09-02 18:53:36 +01:00
Luke Slater
c691ce38ed whoopsie doodle 2014-08-19 00:49:00 +01:00
reality
ad08b24c3e stagger joins 2014-08-18 23:13:55 +00:00
Luke Slater
964467464c [Close #619] 2014-07-27 14:05:21 +01:00
reality
311f8034f4 devoice on quiet 2014-07-23 16:08:05 +00:00
Luke Slater
dd8e1fccd7 Update config.json.sample 2014-07-22 22:20:14 +01:00
reality
30ffd22599 ~squestion 2014-07-22 11:33:36 +00:00
reality
48ba900ea1 new post with less info 2014-07-22 11:28:54 +00:00
reality
45f1b3c311 whoops 2014-07-21 21:34:04 +00:00
reality
70040a0ed3 curr chan ban 2014-07-21 21:31:04 +00:00
reality
7b3489225c radio thing 2014-07-21 13:30:18 +00:00
reality
ba393020b2 include - in #hashtag 2014-07-21 08:56:57 +00:00
reality
776bd6d7d6 dont post radio if undefined 2014-07-19 19:58:22 +00:00
reality
aab0e8b6db better request 2014-07-19 16:57:45 +00:00
reality
80ff91b609 Merge branch 'master' of https://github.com/reality/dbot 2014-07-14 22:54:42 +00:00
reality
ad35f9cc82 change notify event trigger 2014-07-14 22:54:23 +00:00
reality
c0e8a588ec change youtube to yt 2014-07-14 13:42:56 +00:00
reality
60f9c5ef29 change youtube to yt 2014-07-14 13:41:29 +00:00
reality
18f7f64b47 unescape reddit titles for all links 2014-07-12 18:19:59 +00:00
reality
427bb7c4b8 unescape reddit titles 2014-07-12 18:15:56 +00:00
reality
1aec6dcd34 update jsbot 2014-07-11 18:13:59 +00:00
reality
7ca178fced fix merge? 2014-07-08 11:33:56 +00:00
reality
01b2d22059 test 2014-07-07 11:06:38 +00:00
reality
f621fbc38e update jsbot 2014-07-07 10:43:03 +00:00
reality
9fe8b3ab43 #warn 2014-06-29 12:09:47 +00:00
reality
4aed770b7e autoquietwarn 2014-06-26 22:19:18 +00:00
reality
9530bead9c fix web access 2014-06-26 22:03:45 +00:00
reality
5ecd3054b3 move warn to api and rmwarning 2014-06-26 21:50:33 +00:00
reality
1af4788de0 you fool you foolish fool 2014-06-26 19:50:45 +00:00
reality
0697f0b5a0 ~question 2014-06-26 19:40:54 +00:00
reality
610a8cfabf also add a link for removals 2014-06-19 15:44:31 +00:00
reality
3250e25aa0 add link output to qadd 2014-06-19 15:22:25 +00:00
reality
ad0133b948 rss.title not feed.title 2014-06-19 15:11:04 +00:00
reality
3a2374ad59 dont repeat items with the same title within a bound of 30 2014-06-19 14:51:54 +00:00
reality
09e91bf817 Merge branch 'master' of github.com:reality/dbot 2014-06-19 14:42:03 +00:00
reality
daa5c9ccc2 some updates to the rss module 2014-06-19 14:41:57 +00:00
Luke Slater
abab37b71e Merge pull request #618 from RCTrapdoor/patch-3
Update regex.js
2014-06-19 14:33:46 +01:00
RCTrapdoor
71bda91639 Update regex.js
Enable users to correct other users suffixing usernames with either comma or colon.
2014-06-19 14:21:54 +02:00
reality
c50975672e fix 2014-06-16 16:20:26 +00:00
reality
fbdec17670 dont post the thingy if there is no category or author 2014-06-16 15:53:27 +00:00
reality
ecec80ac58 add puush module 2014-06-09 21:03:25 +00:00
reality
f1442edf34 fix web perms 2014-06-06 01:16:30 +00:00
reality
51426264d6 first step of [#616] 2014-06-03 22:47:52 +00:00
Luke Slater
58b9e430ad Merge pull request #615 from amki/rss
Added rss feed module
2014-06-02 17:55:15 +01:00
reality
628b7bc378 140 chars in synopsis 2014-06-02 02:13:04 +00:00
reality
6990a10d9c time thing 2014-05-25 21:16:28 +00:00
reality
131233e7d0 ncount 2014-05-25 20:47:30 +00:00
reality
80e9f245ab what 2014-05-25 19:12:58 +00:00
reality
4f8ce61909 order thethings 2014-05-25 19:03:28 +00:00
reality
569265f1c7 lowercase 2014-05-25 18:24:13 +00:00
reality
4acf17f636 tags 2014-05-25 17:54:46 +00:00
reality
2864c0fa7f whoops 2014-05-25 17:22:08 +00:00
reality
6218def4df tags or whatever 2014-05-25 17:16:53 +00:00
amki
b5a8929931 Fixed outputting multiple posts per check 2014-05-24 22:22:34 +00:00
amki
639467fe27 Fixed crash in addrssfeed 2014-05-24 18:15:12 +00:00
amki
73ce7c6bfd Add feedparser to npm dependencies (rss module) 2014-05-24 17:28:35 +00:00
amki
bc00fabb81 Roi requested the RSS module use goo.gl shortened links 2014-05-24 17:16:46 +00:00
amki
64f5f61a0a Only use one timer to update all feeds. Properly save lastPosted date 2014-05-24 15:26:28 +00:00
amki
61471e80dc Fixed feeds having seperate last posted times 2014-05-24 00:46:01 +00:00
amki
fc4ba61e86 Only post when there is something new, smaller fixes 2014-05-23 23:38:41 +00:00
amki
358e8b95bb Automatic feed reloading and timed requests work 2014-05-23 23:38:41 +00:00
amki
f38af0836c Added RSS module, able to housekeep feeds for now 2014-05-23 23:38:40 +00:00
reality
6b2f2f8484 rt instead of mymovieapi 2014-05-22 16:25:52 +00:00
reality
14bb252a73 Announce module 2014-04-27 21:15:04 +00:00
reality
ef1e514c54 fix report trim 2014-04-18 18:18:23 +00:00
reality
f48e4c3942 something something channel users 2014-04-11 23:19:31 +00:00
reality
5644675b74 double whoops 2014-02-27 01:40:50 +00:00
reality
c97d947087 whoops 2014-02-27 01:37:47 +00:00
reality
0069bdf5a4 goodreads ~books 2014-02-27 01:34:40 +00:00
reality
f2527a6363 fix #614 2014-02-26 23:24:56 +00:00
reality
93022fa4f2 concurrise the imgur pulls 2014-02-26 15:49:44 +00:00
reality
8c93a75e04 add goodreads 2014-02-26 15:10:11 +00:00
reality
4f3b648bdd add prefix 2014-02-20 20:45:10 +00:00
reality
8d407c39b8 use input not params 2014-02-20 20:42:59 +00:00
reality
eb84abb470 change ~calculate to ~wr 2014-02-20 20:42:09 +00:00
reality
c8d46d73a3 fix tvdb link 2014-02-20 20:33:20 +00:00
reality
ee58d0fdb3 t push:Merge branch 'master' of github.com:reality/dbot 2014-02-20 20:27:27 +00:00
reality
b9894b5597 add nerfed fix 2014-02-20 20:27:21 +00:00
Luke Slater
b72562b0bf Update README.md 2014-02-20 20:20:26 +00:00
reality
721ed55598 fix up wolframalpha 2014-02-20 20:19:44 +00:00
reality
03f2ac16b5 everything except wolfram working 2014-02-20 20:10:45 +00:00
thoso
3ea9fb8c68 500px crappy photo fix 2014-02-20 20:50:12 +01:00
thoso
408707157a Unit module fix 2014-02-20 20:25:50 +01:00
reality
9ec94c889a fix the radio 2014-02-20 17:38:58 +00:00
thoso
c6fdd3bdbc 500px random popular photo 2014-02-20 08:40:24 +01:00
thoso
fb14f24927 TVDB module
Added a tvdb module, needs to request apikey. may or may not work due
to tvshow array.
2014-02-20 07:23:25 +01:00
thieson
7f8209544e Merge pull request #2 from reality/master
Update udp.js
2014-02-20 06:37:24 +01:00
thoso
f21745305e Only known users can add quotes
May not work due to callback inside dbot.users.api.isKnownUser(…)
2014-02-20 06:32:46 +01:00
thoso
df87621241 sample fix 2014-02-20 06:09:16 +01:00
thoso
b369ce3a0c Various README.md's and cleanups 2014-02-20 06:06:02 +01:00
thoso
d1b72fe443 Added Sample folder for (easier) module development. 2014-02-20 04:33:56 +01:00
thoso
ade1dcd5e8 typo fix 2014-02-20 01:48:04 +01:00
thoso
ed433977c7 config.json s 2014-02-20 00:56:09 +01:00
thoso
8f8636c509 Added Wolfram Alpha module 2014-02-20 00:52:42 +01:00
thoso
b43be1f900 Adding a unit converter
Due to issue #596 I coded a basic unit converter to convert time,
distance, mass, volume and digital values. It depends on node-units.

Also, fixed a typo.
2014-02-19 22:19:36 +01:00
Luke Slater
3beee89f00 Update udp.js 2014-02-18 18:47:43 +00:00
thieson
7537d85084 requesting music for tripradio
Adding a command to ping the DJ that a user requests a song.
2014-02-18 17:17:39 +01:00
thieson
566c91a2b6 Merge pull request #1 from reality/master
pull
2014-02-18 16:09:41 +01:00
reality
2c1acab761 try abort 2014-02-17 20:51:20 +00:00
reality
a38f8df657 whoops 2014-02-17 20:42:24 +00:00
reality
e4997cd915 fuck timers 2014-02-17 20:31:20 +00:00
reality
0913f382d7 missing bing 2014-02-17 20:13:29 +00:00
reality
21eab082bf fuck lag 2014-02-17 20:11:11 +00:00
reality
748b42cff0 radio 2014-02-17 20:00:31 +00:00
reality
2bb307a462 ahem 2014-02-07 20:45:14 +00:00
reality
33c48f854c nickserv freeze account on ~nban 2014-02-07 15:20:24 +00:00
reality
1751c197b6 fix [#602] 2014-02-04 00:58:09 +00:00
reality
27f26da0e5 Merge branch 'master' of github.com:reality/dbot 2014-02-03 17:37:47 +00:00
reality
39d0bd7058 Quiet no longer uses chanserv. Some cleanup of host code. Should fix [#601] 2014-02-03 17:37:38 +00:00
Luke Slater
8302c83001 close #543 2014-02-02 05:19:48 +00:00
Luke Slater
eaf5e97266 PART ONE #543 I THINK YES 2014-02-02 05:18:44 +00:00
reality
68e13e7bcd fix the poll views 2014-01-29 22:42:16 +00:00
reality
faaee59116 yt https 2014-01-27 01:45:00 +00:00
reality
349f283af6 let the debugging begin 2014-01-25 14:35:53 +00:00
reality
4b8363c9d1 maybe fix 2014-01-10 23:49:33 +00:00
reality
8934ebed1d use voice auth on alt nick 2014-01-06 22:36:56 +00:00
reality
62efc4b2b9 fix ignore for multiple nicks and whatever 2014-01-06 18:19:18 +00:00
reality
36aab19204 only do notify host lookup on given channels in the config 2014-01-06 18:05:44 +00:00
reality
502d9c5ae8 update jsbot 2014-01-06 17:42:01 +00:00
reality
b86efbc3c2 return the correct lastly played game 2014-01-05 03:17:42 +00:00
reality
2204c910b9 that will soh wye 2014-01-04 17:59:14 +00:00
reality
a4a6689d38 update jsbot 2014-01-04 17:08:19 +00:00
reality
b9926a154c fix up the nick before action fix. this should be more efficient (plus it is prettier) 2014-01-04 03:51:31 +00:00
reality
023dafc4cb update jsbot 2014-01-04 03:25:50 +00:00
reality
2fac33c2e7 fix module list on index 2014-01-02 13:11:42 +00:00
reality
02d18245c3 fix aliasing 2013-12-31 16:06:19 +00:00
reality
130113db50 ~server 2013-12-31 01:43:07 +00:00
reality
036cdb0740 fix log ignore hook for setwebpass 2013-12-30 14:16:03 +00:00
reality
5941d3f1bd fix help output 2013-12-29 19:11:44 +00:00
reality
a4009d7ff3 fix regex glober 2013-12-29 18:47:31 +00:00
reality
a554b8d1cf fix all the module regexen 2013-12-29 18:38:24 +00:00
reality
1a5f809bdc fix the commands 2013-12-29 18:23:35 +00:00
reality
2677834aad fix regexen 2013-12-29 18:19:17 +00:00
reality
c73deb5974 fix some recursions in admin 2013-12-29 18:15:16 +00:00
reality
6204df19c0 initial support for optional command prefix. module commands can currently either use ~command or just command as key, since module loader implements legacy support by shifting the key. there may be a couple of bugs too if there are any recursive commands 2013-12-29 18:13:59 +00:00
reality
6d28aa272b fix NICK without talking 2013-12-29 11:09:00 +00:00
reality
8e734d895f that should learn it 2013-12-29 10:39:03 +00:00
reality
2ae2b427b0 what if i specifically ask for json 2013-12-27 16:38:59 +00:00
reality
6817c4568b yep 2013-12-27 16:37:37 +00:00
reality
b74ba66a87 test 2013-12-27 16:36:34 +00:00
reality
e0c2ec36f8 syntax is important 2013-12-27 16:35:42 +00:00
reality
b008f28406 wow 2013-12-27 16:35:04 +00:00
reality
5c34719ad1 db 2013-12-27 16:31:52 +00:00
reality
563a9adaf9 etymology 2013-12-27 16:24:16 +00:00
reality
18780b59b0 add crypto-js to the npm dependencies, merge #600 [Close #600] 2013-12-26 17:09:12 +00:00
amki
76b6371498 Fixed .js comment 2013-12-26 06:59:56 +00:00
amki
4b4f0a1608 Fixed crypto readme 2013-12-26 06:55:21 +00:00
amki
7a51fdf524 Added crypto module 2013-12-26 06:55:12 +00:00
reality
75b4b4e2c2 add process to install 2013-12-24 05:03:06 +00:00
reality
5a676220dc revert 2013-12-24 03:22:04 +00:00
reality
0e46741d9a quiet fix 2013-12-24 03:19:54 +00:00
reality
aaa924d9d8 should fix channel ignores 2013-12-24 00:29:00 +00:00
reality
fe0cbf048b remove debugging. note: offload timers to appropriate clllllosure 2013-12-22 04:09:37 +00:00
reality
148535e7de temp disable the thingie 2013-12-21 23:33:19 +00:00
reality
72ac41d632 that should fix the shitty regex too 2013-12-21 23:24:18 +00:00
reality
ca5357ea49 rekt 2013-12-21 23:19:11 +00:00
reality
4dd774f290 de debug in da lebug 2013-12-21 23:15:05 +00:00
reality
c3ffe8d729 this should clean up the timers properly 2013-12-21 22:58:36 +00:00
reality
47d1f3ea31 reddit debug 2013-12-21 19:00:00 +00:00
reality
633e233d9d hes got a knot in his tail 2013-12-20 13:50:32 +00:00
reality
c1f3905672 destroy ints on destroy 2013-12-19 20:28:27 +00:00
reality
63e1344851 missing bind 2013-12-19 20:16:19 +00:00
reality
3cd6c91eda whoops 2013-12-19 20:11:54 +00:00
reality
0422c3702d do the thing thing 2013-12-19 20:02:50 +00:00
reality
44a3b4e7c6 title fir thing thing 2013-12-19 19:06:17 +00:00
reality
c48832a2bc use the whatsit oh shit 1% battery sjiaofjdisaojfodsa 2013-12-19 18:53:35 +00:00
reality
4089fee13c access control on the thingie whatsit 2013-12-19 18:51:49 +00:00
reality
b0d41fb490 remove the debug output thing 2013-12-19 18:50:23 +00:00
reality
f04da64b21 something something reddit feeds 2013-12-19 18:48:49 +00:00
reality
9c17803e03 this should do it 2013-12-13 00:08:20 +00:00
reality
2cb8c4fc10 this is what you get for not modulating properly you shit 2013-12-12 23:49:40 +00:00
reality
5e1dcd4d30 pass module links to each page 2013-12-12 19:19:44 +00:00
reality
eb348848e6 this commit either a) Closes #598 or b) kills us all. Hold onto your coconuts. 2013-11-29 06:16:33 +00:00
reality
e66df67913 filter out duelling network screenshot SINCE YOU SAID PLEASE 2013-11-25 16:33:27 +00:00
reality
05ebfd1dfd handle reddit short link whatsits 2013-11-20 10:25:05 +00:00
reality
61c4afd8aa jaja 2013-11-19 00:16:17 +00:00
reality
1ef1a6cf34 there was another error 2013-11-04 21:55:10 +00:00
reality
068286744f fix warn strings 2013-11-04 21:46:15 +00:00
reality
3d3203b823 change ~game to ~playing 2013-11-04 19:29:51 +00:00
reality
696aef6a6f lets try this then 2013-11-02 21:51:20 +00:00
reality
2ce0e67d4b well lets give this a try 2013-11-02 21:40:10 +00:00
reality
fc415aa6c6 fix lastfm 2013-10-31 23:11:57 +00:00
reality
39f36e146e fix dent 2013-10-30 17:13:36 +00:00
reality
f45084e01a fixt 2013-10-29 22:45:51 +00:00
reality
734f53d9fa strings of project in german 2013-10-29 22:37:38 +00:00
reality
d093fafbe9 whoopsies 2013-10-29 22:16:16 +00:00
reality
0e72143945 the german language - sponsored by roi 2013-10-29 22:07:20 +00:00
reality
0ebae76732 TRANSLATIONS FROM ROI 2013-10-29 21:39:26 +00:00
reality
5541981d4e whats all this then ~scrobbliest 2013-10-29 20:30:50 +00:00
reality
9d7093e6f2 ~lastfm playcount whatsit 2013-10-29 20:16:08 +00:00
reality
ea0aab80e2 tastiest caching 2013-10-29 16:17:22 +00:00
reality
ba8a603e26 dont compare if lfm is == 2013-10-29 16:03:19 +00:00
reality
13a2c465ae null chekc in profile 2013-10-29 15:52:20 +00:00
reality
1920b4fd03 some debug output for tastiest 2013-10-29 15:49:47 +00:00
reality
93c53b93e2 ~tastiest 2013-10-29 15:45:00 +00:00
reality
41fe000bf4 gooood sam 2013-10-28 17:03:08 +00:00
reality
7c1b1066f9 basic charybdis flag reference for chanserv (well its atheme) and chanmodes 2013-10-28 16:44:37 +00:00
reality
055e865628 games command 2013-10-28 16:11:16 +00:00
reality
e8c0d54b02 bleh 2013-10-28 14:55:34 +00:00
reality
c1ac7f035d sight 2013-10-28 14:53:36 +00:00
reality
1d2a5f891e /home/reality/Projects/dbot/modules/steam/steam.js:101 2013-10-28 14:50:21 +00:00
reality
912df73b56 such crashy 2013-10-28 14:49:09 +00:00
reality
7a4dc61412 whoops 2013-10-28 14:39:44 +00:00
reality
dc0785fe2e currently playing and server info 2013-10-28 14:38:35 +00:00
reality
70a2a77cc0 le derp 2013-10-28 13:59:42 +00:00
reality
0fa228a749 some better output 2013-10-28 13:58:58 +00:00
reality
d910b116c6 basic steam module 2013-10-28 13:43:35 +00:00
reality
07206a0ac3 artists 2013-10-25 15:36:53 +00:00
reality
0fac0ac948 ability to set a custom resolver function on commands to gain info, applied this pattern to lastfm 2013-10-25 15:26:53 +00:00
reality
956b947a4c stupid sexy trapdoor 2013-10-24 22:17:10 +00:00
reality
a28ec17a6d pass some limit params 2013-10-24 20:35:29 +00:00
reality
96926ab5fa fancy ass too complicated suggestion thing + lastfm module rewrite w/ proper api 2013-10-24 20:31:49 +00:00
reality
6d81b21b43 ~taste 2013-10-24 18:59:20 +00:00
reality
cf0c6f19c4 hopefully fix mergeusers for the last time 2013-10-24 16:00:15 +00:00
reality
a1826e5c95 that should do it 2013-10-24 12:33:50 +00:00
reality
8e791ed581 look at what others are listening to, fix output if user doesnt have lastfm proeprty set 2013-10-24 12:30:53 +00:00
reality
614520443f get a youtube link for last fm ~listening. note: clean up yt api later 2013-10-24 09:47:56 +00:00
reality
1c1ba2ef02 strings for lastfm 2013-10-24 09:37:54 +00:00
reality
93b751af29 output prefix 2013-10-24 09:33:48 +00:00
reality
fbf1bc1ae2 distinction between now playing and last playedd 2013-10-23 20:46:32 +00:00
reality
9d48917d09 tings 2013-10-23 20:25:42 +00:00
reality
7f2d813c8c lastfm module 2013-10-23 20:15:23 +00:00
reality
1e995d7bba fix profile 2013-10-23 19:53:14 +00:00
reality
80b76c9325 fucking idiot 2013-10-23 11:46:07 +00:00
reality
b01fce2cc8 [Close #595] 2013-10-23 11:41:22 +00:00
reality
4537651367 lets try that 2013-10-23 10:00:40 +00:00
reality
9369fd8d05 correct the date format 2013-10-22 16:52:30 +00:00
reality
a3f1341daf double whoops 2013-10-22 16:49:30 +00:00
reality
5c0a9d9300 whoops 2013-10-22 16:48:33 +00:00
reality
d044232cd0 date info for sstats 2013-10-22 16:45:52 +00:00
reality
fdfb94371e whoops 2013-10-22 14:45:21 +00:00
reality
de1d8d67d8 no final callback on merged words 2013-10-22 14:43:18 +00:00
reality
1c0b3d5780 apparently undefined is not a function. who knew? 2013-10-22 14:38:10 +00:00
reality
2bd9b081f5 fix missing sstats binds 2013-10-22 14:35:37 +00:00
reality
903427e1ef add word to the actual record 2013-10-22 14:25:53 +00:00
reality
35c59d4c72 report emit 2013-10-22 12:07:38 +00:00
reality
0088d88af6 ensure existence of hosts and tempBans 2013-10-22 08:46:22 +00:00
reality
0649a2c0c9 further closing of #592 2013-10-21 15:40:45 +00:00
reality
632f6e4218 split report up into different files [Close #592] 2013-10-21 15:40:09 +00:00
reality
ead2d21a35 <3 2013-10-20 19:12:07 +00:00
reality
d714b2c0be lowercase all ~word related input 2013-10-20 18:57:15 +00:00
reality
fd400cf3ea small tings 2013-10-20 18:56:39 +00:00
reality
aed155d5a2 ffs 2013-10-20 18:53:27 +00:00
reality
b3a558d81c double derp 2013-10-20 18:53:08 +00:00
reality
f6b8ff9030 derp 2013-10-20 18:51:14 +00:00
reality
f217289e9d strip punctuation 2013-10-20 18:49:19 +00:00
reality
c56c7d2664 lowercase the thing 2013-10-20 18:44:02 +00:00
reality
c372efc3eb wordusers highscore thing 2013-10-20 18:42:00 +00:00
reality
9746641f48 track words and shit yo 2013-10-20 18:29:54 +00:00
reality
e45aa0bcde 24 horu clock to appease roi 2013-10-20 17:55:05 +00:00
reality
a721d82e76 the thing the thing 2013-10-20 17:50:19 +00:00
reality
5a3b936aff fix the regex 2013-10-20 17:48:58 +00:00
reality
2f3c6f9206 ~time command 2013-10-20 17:47:40 +00:00
reality
708e3e0b96 the thing the thing 2013-10-20 16:41:08 +00:00
reality
a2cb958b73 oh it was that easy lol 2013-10-20 16:37:46 +00:00
reality
e40a10dbdc whoops 2013-10-20 16:36:40 +00:00
reality
d568562cad eh that should do it 2013-10-20 16:35:12 +00:00
reality
f492ead7d6 whoops 2013-10-20 16:22:28 +00:00
reality
c611487f42 mite b cool 2013-10-20 16:16:21 +00:00
reality
d0ac927b92 useful error message when attempting to log in with server what dont exist innit 2013-10-20 14:25:43 +00:00
reality
54a0a96cbc ~last thing 2013-10-17 14:31:05 +00:00
reality
44316a5de1 efficiency and command merging 2013-10-17 12:46:55 +00:00
reality
0db0abeb6a shoutiest and wordiest 2013-10-17 11:50:34 +00:00
reality
906291234b ~uncouth 2013-10-17 11:46:50 +00:00
reality
74af9642e1 make it more efficient or whatever 2013-10-17 11:42:05 +00:00
reality
a828536e97 cloudest 2013-10-17 09:16:50 +00:00
reality
10a46a3bde overall ~loudest 2013-10-17 09:10:01 +00:00
reality
bb37adc63d allow curse incrusions 2013-10-17 04:48:45 +00:00
reality
1f3dd159b3 lowercase the whatist 2013-10-17 04:15:12 +00:00
reality
2cd538e1eb words data 2013-10-17 03:59:29 +00:00
reality
ffd60654b1 inception times for all 2013-10-17 03:06:17 +00:00
reality
fc17201f35 derp derp 2013-10-16 15:22:45 +00:00
reality
d7d8c34167 basic sstats module with ~lines and ~clines because i love sundown 2013-10-16 15:12:05 +00:00
reality
96db56a25c exclude common screenshot resolutions for ~ri 2013-10-16 08:15:00 +00:00
reality
785df48d7c prototype for question module 2013-10-15 10:15:49 +00:00
reality
05f2fdb8a3 fix the quote linky thingy on the web whatsit 2013-10-15 06:06:16 +00:00
reality
3df467e35e whoops 2013-10-13 04:51:15 +00:00
reality
439d25f9b0 change ~film to ~imdb 2013-10-13 04:47:58 +00:00
reality
3dfa3be2e1 whops 2013-10-13 04:19:21 +00:00
reality
e9605b60d3 i think this do it 2013-10-13 04:17:17 +00:00
reality
9b036039c5 this may be hilariously broken 2013-10-13 01:25:33 +00:00
reality
110f23ca2f chan_redirs in report [close #591] 2013-10-13 00:29:34 +00:00
reality
56616d6bdf imdb link handler 2013-10-12 22:58:34 +00:00
reality
7c621aeae2 replace rottentomatoes module with new fancier imdb module 2013-10-12 22:31:57 +00:00
reality
2f747d618c basic rotten tomatoes module with a search thing 2013-10-12 21:46:04 +00:00
reality
7046539f29 have both youtube link getters use the same formatLink function 2013-10-08 15:03:38 +00:00
reality
f87f6a37d4 fix ternary and [close [#585]] 2013-10-08 14:51:44 +00:00
RCTrapdoor
e43ead6832 Look at my sexy inline if statement, LOOK AT IT 2013-10-08 16:42:42 +02:00
reality
bf0c277ad6 Change sep. char to comma for YT merge [Close #589] 2013-10-08 13:08:57 +00:00
RCTrapdoor
b6d7a92b32 Padding left zero on seconds <10, (5:09 instead of 5:9) as well as thousands separator on viewCount
I'm not entirely sure if the thousands separator will work like that, but there's only one way to find out.
2013-10-08 10:49:33 +02:00
reality
f8e4c52616 new jsbot 2013-09-24 21:20:03 +00:00
reality
6e1238206d change default warn access to power user 2013-09-24 11:52:23 +00:00
reality
9e98f2799d remove the bs formatting shit from run innit 2013-09-24 01:35:25 +00:00
reality
93915c7c5f derp 2013-09-16 02:07:37 +00:00
reality
bf996f2fd1 that should do it 2013-09-13 14:54:17 +00:00
reality
ef4ceac61e fix crash when imgur shit innit 2013-09-13 14:35:19 +00:00
reality
3ff165f323 that should do it.. maybe.. 2013-09-10 18:25:20 +00:00
reality
a3f9e238bf logging fix + caching crash fix 2013-09-10 18:11:39 +00:00
reality
fbc0aa59c2 this should be somewhat faster at filling the cache 2013-09-10 17:21:05 +00:00
reality
d271b6672f ri cache, replace ri with ssri, move ri to lri 2013-09-10 17:07:34 +00:00
reality
df7d4149c6 ~ssri 2013-09-10 16:29:23 +00:00
reality
46de1a5fac present for roi 2013-09-09 19:30:23 +00:00
reality
64fdca5097 add time to ythe youtube module 2013-09-09 18:37:15 +00:00
Luke Slater
4f36d2608f Update strings.json
add time to yt thing
2013-09-09 19:35:05 +01:00
reality
6ded78bb5e LAME 2013-09-09 18:33:46 +00:00
reality
e4b46452e5 make the index links a bit nicer 2013-09-08 16:25:20 +00:00
reality
3089d2bd1b that will do for now [#585] 2013-09-08 11:20:48 +00:00
reality
c97347a228 that will do for now [#585] 2013-09-08 11:20:37 +00:00
reality
1f0c11e8c3 derp#2 2013-09-08 10:54:30 +00:00
reality
8db41809fd whoops 2013-09-08 10:52:53 +00:00
reality
e80b695dfa crash error reporting [#468] 2013-09-08 10:51:21 +00:00
reality
c46f5c4f9b fix nunban 2013-09-07 23:02:04 +00:00
reality
7ec7537b7d include user in missing notifies 2013-09-07 19:28:46 +00:00
reality
17bd41d3ef fix the chain 2013-09-07 18:12:02 +00:00
reality
6b9c7d6cf0 that should be channel.nicks 2013-09-07 18:03:59 +00:00
reality
a238994e0f voiced in chan can quiet 2013-09-07 17:55:48 +00:00
reality
4940eec5ce i think this fix it 2013-09-02 21:32:46 +00:00
reality
b1c41008ce ~~-nick-~~ 2013-08-31 22:57:29 +00:00
reality
306b28bf93 more translations from gian-maria.daffre@giammi.com 2013-08-31 19:58:17 +00:00
reality
b4531e2c8f usage for quiet 2013-08-31 16:56:24 +00:00
reality
9848785d38 do that thing wot i said in [#581] 2013-08-31 16:25:11 +00:00
reality
24ccdeac8f store channel in missing notifies 2013-08-31 15:47:56 +00:00
reality
f9ca9a76d9 fix [#580] 2013-08-31 15:38:57 +00:00
reality
503326be04 fix missing link 2013-08-31 15:23:27 +00:00
reality
0cd9e6e343 sidething 2013-08-31 15:20:38 +00:00
reality
3b304bf769 add the pm bit 2013-08-31 13:33:10 +00:00
reality
dbccd67b20 why do colons and semicolons look so similar 2013-08-31 13:31:45 +00:00
reality
d7640f1c20 include channel in logs 2013-08-31 13:30:24 +00:00
reality
1675700073 default timezone 2013-08-31 12:57:34 +00:00
reality
6b128ec798 finish [#578] 2013-08-31 12:53:45 +00:00
reality
8e4ac8d503 do that thing wot i said for [#578] 2013-08-31 12:38:25 +00:00
reality
16b9e0e415 link to the things right [#577] 2013-08-31 12:23:50 +00:00
reality
9e67c94074 do that thing what i said for [#577] 2013-08-31 12:17:06 +00:00
reality
c09e9de9f9 remove the old install file 2013-08-31 12:00:29 +00:00
Luke Slater
a90083b287 Merge pull request #576 from iceTwy/master
Update installation files, add default redis port for listening
2013-08-30 22:05:48 -07:00
iceTwy
0ea80369cf install must be run with bash, not sh 2013-08-31 03:15:20 +02:00
iceTwy
26821a3ac5 install must be run with bash, not sh 2013-08-31 03:14:18 +02:00
iceTwy
7bc6fc988f Update node.js module dependencies 2013-08-31 03:11:30 +02:00
Luke Slater
017822d422 Update install.sh 2013-08-30 12:27:33 +00:00
reality
de3eb90ab5 ok i fixed it 2013-08-30 02:55:20 +00:00
reality
3ce44418e7 fix eat cheese for twistington 2013-08-30 02:47:44 +00:00
reality
8b8400f437 notfy fixy 2013-08-29 21:35:05 +00:00
reality
20e76c3076 fix for the overflow 2013-08-29 21:32:48 +00:00
reality
6d1d5d0afe done [#575] 2013-08-29 21:12:45 +00:00
reality
26bce2e8a0 fix for currentnick 2013-08-29 20:07:30 +00:00
reality
2f2c702c8f fix ~setaliasparents, implement mobile aliases [#573] 2013-08-28 20:22:56 +00:00
reality
c5a3d75d29 fix warn 2013-08-28 17:35:11 +00:00
reality
4b36fc33b1 fix the ban host thing 2013-08-28 12:25:33 +00:00
reality
8aa9842091 lc 2013-08-27 23:32:31 +00:00
reality
d718b81d79 bleh 2013-08-27 23:23:33 +00:00
reality
83a37a88f9 o dats coo 2013-08-27 22:52:26 +00:00
reality
0ced8cf27c fix report output 2013-08-27 22:09:59 +00:00
reality
e6b764bd25 cname not channel 2013-08-27 22:08:28 +00:00
reality
78860ce01a oh jesus [#571] 2013-08-27 22:01:44 +00:00
reality
5cdd2d927c well let us see 2013-08-25 23:20:14 +00:00
reality
4a3dbd8f3f um that should do it 2013-08-25 23:09:34 +00:00
reality
ee4ee92340 314 fallback for hostmask 2013-08-25 23:04:46 +00:00
reality
d3d25164c3 change offline time getuserhose 2013-08-25 22:56:19 +00:00
reality
5de0e8ad7d apparently it was 2013-08-25 22:37:31 +00:00
reality
397b732341 could it be? 2013-08-25 22:36:06 +00:00
reality
dd7abf2fff more debugging 2013-08-25 22:32:51 +00:00
reality
fdd3187c82 idfk 2013-08-25 22:22:02 +00:00
reality
884158c27e test 2013-08-25 22:16:12 +00:00
reality
223a000c91 more debug 2013-08-25 22:14:39 +00:00
reality
1c31d46d58 debug 2013-08-25 22:11:26 +00:00
reality
51a7cbaffa i guess 2013-08-25 22:05:50 +00:00
reality
459b6917b5 sigh 2013-08-25 21:59:06 +00:00
reality
96369ab604 split on \n 2013-08-25 20:58:11 +00:00
reality
69f061c671 pick random result for ~imgur 2013-08-25 20:45:47 +00:00
reality
cb38352519 remove imgur from the search terms lol (fix regexen) 2013-08-25 19:07:11 +00:00
reality
ab9cfb1469 fix for not found 2013-08-25 19:04:42 +00:00
reality
760780537a imgur keyword search 2013-08-25 19:02:34 +00:00
reality
ec5ef85023 fucking dumbass 2013-08-25 16:13:07 +00:00
reality
ecd30eb896 forgot a bind..... 2013-08-25 16:10:43 +00:00
reality
5cf79312f4 some more efficiency improvements in users 2013-08-25 15:37:32 +00:00
reality
31f32ca8da performance enhancements to resolveUser [Close #570] 2013-08-25 14:56:00 +00:00
reality
36aae7cad1 timer 2013-08-24 23:11:34 +00:00
reality
f664ba5e5f lol 2013-08-24 23:06:53 +00:00
reality
103407634d counters for channels too 2013-08-24 20:55:57 +00:00
reality
9a4111c393 ~mergeusershook for notify 2013-08-24 20:42:30 +00:00
reality
dfde53b8ac fix the host matching thing and make the regex sexier 2013-08-24 20:27:44 +00:00
reality
abee98b68d revere time order for notifies 2013-08-24 19:53:10 +00:00
reality
14b1727122 didnt mean to push that yet 2013-08-24 19:42:34 +00:00
reality
a6ff79e5cd fix missing entry link 2013-08-24 19:39:02 +00:00
reality
55ff01a618 save before reload 2013-08-24 19:19:58 +00:00
reality
a10a6bf9e6 that should do it 2013-08-24 19:08:43 +00:00
reality
2ab019008f whoops 2013-08-24 19:05:01 +00:00
reality
5358d1be96 thatshoulddo it 2013-08-24 19:03:13 +00:00
reality
2263f884f0 removedebug 2013-08-24 18:59:43 +00:00
reality
e56eaa7a9f debug 2013-08-24 18:56:23 +00:00
reality
08b46b9a8b fixthe entry issuething 2013-08-24 18:54:42 +00:00
reality
de54ec5c61 [close #569] 2013-08-24 18:32:52 +00:00
reality
c397ba40f4 run next anyway 2013-08-24 17:58:17 +00:00
reality
83ffee2874 1 not 0 2013-08-24 17:56:54 +00:00
reality
570239cccf whoopsies 2013-08-24 17:56:11 +00:00
reality
3e8ba74459 view notifies by user [#568] 2013-08-24 17:53:16 +00:00
reality
ec72489300 do [#568] 2013-08-24 17:39:04 +00:00
reality
16607a68d2 revert notifies changes 2013-08-24 15:05:40 +00:00
reality
5b571a0cb8 another test 2013-08-24 15:04:27 +00:00
reality
51e5f620bf test 2013-08-24 15:02:48 +00:00
reality
779ace09bf search by user & remove debug output 2013-08-24 14:49:19 +00:00
reality
1d73115c33 search box for the notificatons [close #563] 2013-08-24 14:46:38 +00:00
reality
904b7d22d5 order notifies right [close #567] 2013-08-24 14:32:27 +00:00
reality
13ff92899c Merge branch 'master' of github.com:reality/dbot 2013-08-24 13:56:53 +00:00
reality
405b3d47e4 Create addIndexLink api func for web to link to index not based on module name. Correct titles for notifications + bans. Also hopefully fix the fineWebPerms access for PUs/Mods [close #565] 2013-08-24 13:56:45 +00:00
reality
a40abd4bbd and trim it 2013-08-24 13:31:12 +00:00
reality
4f4cdea951 this thing 2013-08-24 13:30:17 +00:00
reality
7eec11197a thingy thingy 2013-08-24 13:28:46 +00:00
reality
165c9491a9 herpo derpo 2013-08-21 21:44:10 +00:00
reality
1ff9718039 coloiurs and format change for ~report 2013-08-21 21:10:06 +00:00
reality
91d6f64520 whoops 2013-08-21 20:38:51 +00:00
reality
a5eda46339 some fixes for punctuation 2013-08-21 19:57:19 +00:00
reality
cc52b81eb2 there we go 2013-08-21 19:51:41 +00:00
reality
7bbea38454 fuck youuuuuuu 2013-08-21 19:32:17 +00:00
reality
f3e18f63bb i think that should do it 2013-08-21 19:30:01 +00:00
reality
6d4291c564 fixy fixy 2013-08-21 19:28:33 +00:00
reality
66ead26635 cool if it works 2013-08-21 19:26:45 +00:00
reality
18d67e0f26 space | $ 2013-08-21 19:18:00 +00:00
reality
dee39dd14a derp 2013-08-21 19:16:23 +00:00
reality
fc345f4ebc colour tags 2013-08-21 19:14:55 +00:00
reality
46231c9e56 colours for the type thing 2013-08-21 19:10:10 +00:00
reality
73ca19ca73 dont use coloured strings as db keys lololo 2013-08-21 18:34:31 +00:00
reality
0bcf220d91 fully qualified colours link 2013-08-21 18:32:12 +00:00
reality
e5818e2f04 fix 2013-08-21 18:26:36 +00:00
reality
ddc4b099ee t:wqMerge branch 'master' of github.com:reality/dbot 2013-08-21 18:12:51 +00:00
reality
493f29ff57 colours for ~notify [#560] 2013-08-21 18:12:43 +00:00
Luke Slater
15d5c1ce16 Merge pull request #561 from kabniel/patch-1
Update link.js
2013-08-21 10:37:54 -07:00
kabniel
e34b46d4e4 Update link.js
change title regex to be non-greedy
2013-08-21 19:24:04 +02:00
reality
509911aae0 reset that shit 2013-08-21 15:38:43 +00:00
reality
a9fdd82911 how about this 2013-08-21 15:32:32 +00:00
reality
4d130df390 mroe thingy 2013-08-21 15:27:30 +00:00
reality
284a316cd5 thing 2013-08-21 15:26:16 +00:00
reality
fc4537bcea debugging 2013-08-21 15:22:05 +00:00
reality
0efa8d0217 that might do it 2013-08-21 15:18:08 +00:00
reality
2c988b0f77 i love shots in the dark 2013-08-21 15:13:58 +00:00
reality
71b7b3c570 that might do it 2013-08-21 15:11:06 +00:00
reality
f2755468e0 reality is a dumbass 2013-08-21 14:58:43 +00:00
reality
506ca60682 onyl accept ~ri images bigger than 300x300 2013-08-21 14:55:44 +00:00
reality
d71906838d change notify output format [#560] 2013-08-19 18:21:10 +00:00
reality
e1c8966d9f missing bind 2013-08-19 18:00:56 +00:00
reality
1d5f635f24 werent clearing pre-emit hooks on reload DUMBASS 2013-08-19 17:57:02 +00:00
reality
74ec643208 save debug 2013-08-19 17:51:55 +00:00
reality
a7b2ba96e7 remove extra JOIN listener in users. derp 2013-08-19 17:32:19 +00:00
reality
0f53693c28 implement rUser cache [#559] 2013-08-19 17:11:07 +00:00
reality
5b82ab8690 double derp 2013-08-19 16:57:19 +00:00
reality
68902d74bd derp 2013-08-19 16:54:11 +00:00
reality
c86dc4035b update jsbot for the thingy thing 2013-08-19 16:50:52 +00:00
reality
fea9932631 re-add new_channel_user event in new user tracker (derp) 2013-08-19 08:28:40 +00:00
reality
9a5a75c1c2 set requireweblogin for bans 2013-08-18 20:31:03 +00:00
reality
0739997a59 thingy thiny 2013-08-18 20:28:31 +00:00
reality
fe6141ea81 web part for ban list [#557] 2013-08-18 20:23:13 +00:00
reality
5233481992 dont store bans in quotes, store ban records [#557] 2013-08-18 20:10:19 +00:00
reality
239e94fe05 automatically post version on greload and fix removealias bug 2013-08-18 19:55:06 +00:00
reality
74558aea20 rewrite nick tracking basically, remove a bunch of unused shite [#537] 2013-08-18 19:36:53 +00:00
reality
e04ffa5f25 dope 2013-08-18 17:46:57 +00:00
reality
c513e8cd33 ignore certain commands for log [#556] 2013-08-18 17:42:23 +00:00
reality
e5c51682fb fine tuned web permissions [close #553] 2013-08-18 17:32:57 +00:00
reality
45eb778560 derp 2013-08-18 16:39:35 +00:00
reality
930ca1fbc0 missing log and custom log messages [close #555] 2013-08-18 16:37:30 +00:00
reality
403fd035a7 notify upgrades and whatnot 2013-08-18 15:19:15 +00:00
reality
656861f6df update jsbot for jsbot/#14 fix 2013-08-17 21:28:03 +00:00
reality
0d927881b4 do [close #552] 2013-08-17 21:04:42 +00:00
reality
da025dafbb ability to supple reason with ~quiet [#551] 2013-08-17 19:52:07 +00:00
reality
fa1634d040 unquiet notifies 2013-08-17 19:19:09 +00:00
reality
de8285e199 fix getChannel references 2013-08-17 19:07:48 +00:00
reality
152db6995f add notifications for quiet [close #550] 2013-08-17 19:01:05 +00:00
reality
6fb7c8e91f fix the quote/command correct order thing 2013-08-17 18:13:38 +00:00
reality
52d38aa1e7 create ~removealias command [Close #549] 2013-08-17 17:23:43 +00:00
reality
9d6b2cc3e1 fix ~mergeusers, create getChannel and resolveChannel distinction [#548] 2013-08-17 17:09:55 +00:00
reality
06053b9e17 update currentNick in event pre-emit [close #547] 2013-08-17 16:35:57 +00:00
reality
897b27f31e whatever 2013-08-13 18:30:25 +00:00
reality
98782fed57 update jsbot 2013-08-13 18:29:05 +00:00
reality
e086afc4d8 update jsbot 2013-08-13 18:14:01 +00:00
reality
f6c1f22b4f prevent multiple notifies for missing messages 2013-08-13 17:53:30 +00:00
reality
745a763b7f return after giving command suggestion 2013-08-12 23:32:23 +00:00
reality
7517540cc5 derp 2013-08-12 23:18:40 +00:00
reality
0a212bab1d some command correction shiz 2013-08-12 23:14:44 +00:00
reality
2ed6488d77 derp 2013-08-12 22:25:41 +00:00
reality
e757ee34df fix web routes 2013-08-12 22:14:14 +00:00
reality
a3d18884aa remove a bunch of debugging output 2013-08-12 21:59:38 +00:00
reality
154a0b30fe Modified missing report system as per [#545] 2013-08-12 21:58:30 +00:00
reality
e06c1647c4 fix ~warnings output 2013-08-12 17:17:38 +00:00
reality
3136db5cb4 fix youtube issue 2013-08-11 17:33:47 +00:00
reality
b7ccc8c0e6 even betterer 2013-08-07 19:17:30 +00:00
reality
4dd6fab12f more youtube debugging 2013-08-07 19:11:07 +00:00
reality
bc23d64686 bleh 2013-08-05 22:48:51 +00:00
reality
c0865b1bea increase notify delay? 2013-08-05 22:33:10 +00:00
reality
eb0a61c25b fix offline notifies 2013-08-05 22:18:47 +00:00
reality
99eb1462ad youtube debug output 2013-08-05 22:11:25 +00:00
reality
e9c6729894 remove trailing spaces from report reasons 2013-08-05 19:51:38 +00:00
497 changed files with 27513 additions and 2212 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
# Ignore the user config files
config.json
db.json
# ignore npm

8
.gitmodules vendored
View File

@ -1,6 +1,6 @@
[submodule "jsbot"]
path = jsbot
url = git://github.com/reality/jsbot.git
path = jsbot
url = git://github.com/reality/jsbot.git
[submodule "modules/github"]
path = modules/github
url = git://github.com/zuzak/dbot-github.git
path = modules/github
url = git://github.com/zuzak/dbot-github.git

View File

@ -1,3 +1,47 @@
### dbot - revived
The motivation for this fork came from trying to make the trusty bot of our IRC network work with new infrastructure, until all features made their way into its successor.
Note that this project is not production worthy - it contains a lot of hardcoded debug lines and untidy code. Whether it will see prettification anytime soon, I cannot tell.
So far, the following issues were tackled successfully:
- NickServ identification
- NickServ user retrieval
- Quote shortcuts (~foo)
- Channel statistics (cstats)
This makes the bot usable on an IRC network powered by the Ergo IRCd, which is dbot was not happy communicate with at all when I first tried to run it.
The following issues are known, but are assigned low priority:
- No PASS/SASL authentication - it is hackable with a PASS line right after the USER command, however that prevents some parts of the bot from initializing. To use NickServ authentication, allowing the bot to start properly, the strict username checking feature in Ergo needs to be disabled
- Lots of ENOENT errors on startup
- Channel mode detection and assignment
- Some sites in the webinterface don't load / show errors
The following issues are known, and await investigation:
- Upon issuing some administrative commands, apparently a sort of "queue flush" gets trigered, causing dozens of unban events to make the bot unresponsive for a while
The following issues are resolved, but are not available in this repository, due to files not being accessible:
- WHO parsing for user management commands
- nban/nunban, with newly added ipban
The directory modules-stock/ is the stock modules directory, kept for safekeeping.
The directory modules/ contains changed modules.
The jsbot/ directory is not populated in the stock repository, however its contents are included here, as a few essential changes have been implemented there as well.
Tested with node v10.
Note: TripSit IRC related modules are to be found in a seperate repository.
---
### Original README.md
# DBot IRC Bot
## Introduction
@ -26,7 +70,7 @@ Once you have that set up, you can install DBot's dependencies, configure and
run the bot for the first time with the following command:
```
sh install.sh
./install
```
## Upgrading

View File

@ -15,10 +15,13 @@
"admins": [ "batman" ],
"moderators": [ "whatever" ],
"power_users": [],
"moduleNames": [ "ignore", "admin", "command", "dice", "js", "kick", "quotes", "spelling", "youare", "timers", "stats", "users", "link" ],
"moduleNames": [ "ignore", "admin", "command", "dice", "js", "kick", "quotes", "spelling", "users", "youare", "timers", "sstats", "link" ],
"language": "en",
"debugMode": false,
"debugLevel": 1,
"dbType": "redis",
"redisPort": 6379,
"timezone": "Europe/London",
"repoRoot": "https://github.com/reality/depressionbot/",
"version": "Depressionbot IRC bot 0.5-dev - Lovingly crafted by The DepressionBot Foundation (a charity arm of the Official Aberystwyth Open Source International Development League)."
}

View File

@ -8,7 +8,7 @@ var databank = require('databank'),
*/
var DatabaseDriver = function(config) {
this.config = config;
this.databanks = {};
this.databank = null;
};
/**
@ -20,12 +20,12 @@ DatabaseDriver.prototype.createDB = function(name, driver, schema, callback) {
if(driver == 'redis' && _.has(this.config, 'redisPort')) params.port = this.config.redisPort;
if(driver == 'disk') params.dir = 'db';
this.databanks[name] = Databank.get(driver, params);
this.databanks[name].connect({}, function(err) {
this.databank = Databank.get(driver, params);
this.databank.connect({}, function(err) {
if(err) {
console.log('Didn\'t manage to connect to the data source - ' + err);
} else {
callback(this.databanks[name]);
callback(this.databank);
}
}.bind(this));
};

48
install Executable file
View File

@ -0,0 +1,48 @@
#!/bin/bash
cat LICENCE
git submodule init
git submodule update
command -v node > /dev/null
if [[ $? -gt 0 ]]; then
echo 'node.js is not installed. Please install it before running install.sh.'
exit 1
fi
command -v npm > /dev/null
if [[ $? -gt 0 ]]; then
echo 'npm is not installed. Please install it before running install.sh'
exit 1
fi
npm install googlemaps humanize feedparser node-units tvdb method-override 500px process async wordnik node-uuid underscore request request-promise-native sandbox express moment-timezone moment jade databank databank-redis ent passport passport-local password-hash connect-flash
cd public/
wget https://github.com/twbs/bootstrap/releases/download/v3.3.2/bootstrap-3.3.2-dist.zip
unzip bootstrap-3.3.2-dist.zip
mv bootstrap-3.3.2-dist bootstrap
rm bootstrap-3.3.2-dist.zip
mkdir d3
cd d3
wget https://github.com/mbostock/d3/releases/download/v3.5.5/d3.zip
unzip d3.zip
rm d3.zip
cd ../..
if [ ! -f config.json ];
then
echo 'Creating configuration file...'
cp config.json.sample config.json
$EDITOR config.json # Open config file with EDITOR variable, but if not available:
if [[ $? != 0 ]]; then vim config.json; fi # Find the return code from previous command, if failed then use vim to edit.
fi
read -p "Setup complete. Run dbot now? [y/N]"
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
echo 'Okay. To run the bot, use "node run.js"'
exit
fi
node run.js

View File

@ -1,46 +0,0 @@
#!/bin/bash
cat LICENCE
git submodule init
git submodule update
if [ ! -e /usr/bin/node ] && [ ! -e /usr/local/bin/node ];
then
echo 'node.js is not installed. Please install it before running install.sh.'
exit 1
fi
if [ ! -e /usr/bin/npm ] && [ ! -e /usr/local/bin/npm ];
then
echo 'npm is not installed. Please install it before running install.sh'
exit 1
fi
npm install async wordnik node-uuid underscore request sandbox express moment jade databank databank-redis ent
cd public/
wget http://twitter.github.com/bootstrap/assets/bootstrap.zip
unzip bootstrap.zip
rm bootstrap.zip
mkdir d3
cd d3
wget http://d3js.org/d3.v3.zip
unzip d3.v3.zip
rm d3.v3.zip
cd ../..
if [ ! -f config.json ];
then
echo 'Creating configuration file...'
cp config.json.sample config.json
vim config.json
fi
read -p "Setup complete. Run depressionbot now? [y/N]"
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
echo 'Okay. To run the bot, use "node run.js"'
exit
fi
node run.js

1
jsbot

@ -1 +0,0 @@
Subproject commit 87b8dbbb9083d2ded4b2214a450a58ef09d4e4f9

40
jsbot/README.md Normal file
View File

@ -0,0 +1,40 @@
## JSBot
JSBot is an IRC bot library written in Node JS.
With features like multiple server support and being 'pretty good, I guess,'
JSBot is designed to be the IRC bot library of the future! For an example of a
large project which uses JSBot, take a look at
[DepressionBot](http://github.com/reality/depressionbot/ "DepressionBot").
To get started with JSBot, take a look at the 'run.js' example provided with the
code, then head on over to the
[online documentation](https://github.com/reality/jsbot/wiki/Documentation "JSBot Docs").
## ChangeLog
### 0.3 I guess
* Fixed an edge case with the IRC line tokenisation /potentially/ causing events to be parsed twice
* Isolated all core channel/nick list logic in JOIN/PART/KICK/QUIT/NICK handlers
* Removed useless timeout in 004 handler, switched it to handle 001 instead
* Removed a duplicate send() call for IDENTIFY
* Semantically reorganised source code
* Various other improvements
speeddefrost <3
### 0.2
* Multiple server support.
* Functionality for certain users to ignore listeners with certain tags.
* Better 'event' object passed to listeners.
* Ability to 'reply' to events.
### 0.1
* It connects to a server
* Listeners
* Ping/Pong
* Some of the other functionality you'd expect, like, what do you want from me?

572
jsbot/jsbot.js Normal file
View File

@ -0,0 +1,572 @@
var _ = require('underscore')._,
net = require('net'),
async = require('async'),
tls = require('tls'),
Tokenizer = require('./tokenizer');
var JSBot = function(nick) {
this.nick = nick;
this.connections = {};
this.ignores = {};
this.preEmitHooks = [];
this.events = {
'JOIN': [],
'PART': [],
'QUIT': [],
'NICK': [],
'PRIVMSG': [],
'MODE': [],
'KICK': []
};
this.addDefaultListeners();
};
// connections
var Connection = function(name, instance, host, port, owner, onReady, nickserv, password, tlsOptions) {
this.name = name;
this.instance = instance;
this.host = host;
this.port = port;
this.owner = owner;
this.onReady = onReady;
this.nickserv = nickserv;
this.password = password;
this.tlsOptions = tlsOptions;
this.channels = {};
this.commands = {};
this.encoding = 'utf8';
this.netBuffer = '';
this.conn = null;
this.lastSent = Date.now();
};
Connection.prototype.connect = function() {
if((typeof this.port == 'string' || this.port instanceof String) && this.port.substring(0, 1) == '+') {
this.conn = tls.connect(parseInt(this.port.substring(1)), this.host, this.tlsOptions);
} else {
this.conn = net.createConnection(this.port, this.host);
}
console.log('trying to connect' + this.port + ' ' + this.host);
this.conn.setTimeout(60 * 60 * 1000);
this.conn.setEncoding(this.encoding);
this.conn.setKeepAlive(enable=true, 10000);
connectListener = function() {
this.send('NICK', this.instance.nick);
//this.send('USER', this.instance.nick, '0', '*', this.instance.nick);
this.send('PASS ' + this.instance.nick + ':' + this.password);
this.send('USER', this.instance.nick + ' ' + this.instance.nick + ' ' + this.instance.nick, ' ', this.instance.nick);
}.bind(this);
this.conn.addListener('connect', connectListener.bind(this));
this.conn.addListener('secureConnect', connectListener.bind(this));
this.conn.addListener('error', function(err) {
console.log('hi')
console.log(err);
console.error(err);
});
this.conn.addListener('data', function(chunk) {
this.netBuffer += chunk;
console.log(chunk)
var t = new Tokenizer(this.netBuffer);
while(true) {
var line = t.tokenize('\r\n');
if(line == null) {
this.netBuffer = t.tokenize(null);
break;
}
this.instance.parse(this, line);
}
}.bind(this));
};
Connection.prototype.send = function() {
var message = [].splice.call(arguments, 0).join(' ');
//if(Date.now() > this.lastSent + 500) {
message += '\r\n';
this.conn.write(message, this.encoding);
this.lastSent = Date.now();
//} else {
/* setImmediate(function() {
this.send(message);
}.bind(this));
}*/
};
Connection.prototype.pong = function(message) {
this.send('PONG', ':' ); //+ message.split(':')[1]);
};
Connection.prototype.join = function(channel) {
this.send('JOIN', channel);
};
Connection.prototype.part = function(channel) {
this.send('PART', channel);
};
JSBot.prototype.addConnection = function(name, host, port, owner, onReady, nickserv, password, tlsOptions) {
tlsOptions = tlsOptions || {};
tlsOptions = _.defaults(tlsOptions, {rejectUnauthorized: false});
this.connections[name] = new Connection(name, this, host, port, owner, onReady,
nickserv, password, tlsOptions);
};
JSBot.prototype.connect = function(name) {
var conn = this.connections[name];
this.addListener('001', 'onReady', function(event) {
conn.instance.say(conn.name, conn.nickserv, 'IDENTIFY ' + this.nick + " " + conn.password);
setTimeout(function() {
if(conn.onReady != null)
conn.onReady(event);
}, 5000);
});
conn.connect();
};
JSBot.prototype.connectAll = function() {
_.each(this.connections, function(connection, name) {
this.connect(name);
}, this);
};
// event parsing and processing
JSBot.prototype.parse = function(connection, input) {
var event = new Event(this),
t = new Tokenizer(input);
event.server = connection.name;
event.allChannels = this.connections[event.server].channels;
if(input[0] == ':') {
// consume to next whitespace, strip leading ':'
var prefix = t.tokenize(' '),
maskMatch = prefix.match(/:(.+)!(.+)@(.+)/);
if(maskMatch && maskMatch.length == 4) {
event.user = maskMatch[1];
event.ident = maskMatch[2];
event.host = maskMatch[3];
}
else {
event.host = prefix.substring(1);
}
}
/* parameter string extraction */
// try consuming to beginning of a message
var paramsStr = t.tokenize(' :');
if(!paramsStr) {
// if that fails (no message), fall back to line ending
paramsStr = t.tokenize(null);
} else {
// first attempt succeeded, extract message
event.message = t.tokenize(null);
}
// split the parameter string
event.args = paramsStr.split(' ');
// use first item as action, remove from list
event.action = event.args.shift();
// -- Common Event Variables --
// All channel/nick/target parameters in server-to-client events are accounted for here.
// Others need to be handled manually via event.params.
if (/^\d+$/.test(event.action)) {
var rsp = parseInt(event.action),
nickRsps = [ 301, 311, 312, 313, 317, 318, 319, 314,
369, 322, 324, 338, 401, 406, 432, 433, 436 ],
channelRsps = [ 322, 324, 331, 332, 346, 347, 348, 349,
366, 367, 368, 403, 404, 405, 467, 471,
473, 474, 475, 476, 477, 478, 482 ],
channelNickRsps = [ 325, 341 ],
targetRsps = [ 407, 437 ];
if(nickRsps.indexOf(rsp) != -1) {
event.user = event.args[0];
}
else if(channelRsps.indexOf(rsp) != -1) {
event.channel = event.args[0];
}
else if(channelNickRsps.indexOf(rsp) != -1) {
event.channel = event.args[0];
event.user = event.args[1];
}
else if(targetRsps.indexOf(rsp) != -1) {
if ('&#!+.~'.indexOf(event.args[0][0]) != -1) {
event.channel = event.args[0];
} else {
event.user = event.args[0];
}
}
else if(rsp == 352) {
event.channel = event.args[0];
event.user = event.args[4];
}
else if(rsp == 353) {
event.channel = event.args[2];
}
else if(rsp == 441) {
event.user = event.args[0];
event.channel = event.args[1];
}
}
else {
if(event.action == 'PRIVMSG') {
if('&#!+.~'.indexOf(event.args[0][0]) != -1) {
event.channel = event.args[0];
}
}
else if(event.action == 'JOIN' ||
event.action == 'PART' ||
event.action == 'TOPIC')
{
event.channel = event.args[0];
}
else if(event.action == 'KICK') {
event.channel = event.args[0];
event.targetUser = event.args[1];
}
else if(event.action == 'NICK') {
event.newNick = event.args[1];
event.multiChannel = true;
}
else if(event.action == 'MODE') {
event.channel = event.args[0];
event.modeChanges = event.args[1];
if(event.args.length > 2) {
event.targetUsers = event.args.slice(2);
}
}
else if(event.action == 'QUIT') {
event.multiChannel = true;
}
if(event.multiChannel) {
// populate a list of channels this event applies to
event.channels = [];
for(var ch in event.allChannels) {
for(var nick in event.allChannels[ch].nicks) {
if(nick == event.user) {
event.channels.push(event.allChannels[ch]);
}
}
}
}
else if(event.channel && event.channel in event.allChannels) {
// replace the channel name with it's coresponding object
event.channel = event.allChannels[event.channel];
} else {
event.channel = {
'name': event.user,
'nicks': {},
'toString': function() {
return this.name;
}
}
}
}
// Run any pre-emit hooks
async.eachSeries(this.preEmitHooks, function(hook, callback) {
hook(event, callback);
}, function(err) {
this.emit(event);
}.bind(this));
// for handlers
if(event.message) {
event.params = event.message.split(' ');
} else {
event.params = [];
}
};
JSBot.prototype.addPreEmitHook = function(func) {
this.preEmitHooks.push(func);
};
JSBot.prototype.clearHooks = function() {
this.preEmitHooks = [];
};
JSBot.prototype.emit = function(event) {
if(event.action in this.events) {
_.each(this.events[event.action], function(listener) {
var eventFunc = listener.listener;
var channel = false;
if(event.channel) {
channel = event.channel.name;
}
if(_.isFunction(eventFunc) && this.ignores &&
(_.has(this.ignores, event.user) && _.include(this.ignores[event.user], listener.tag)) == false &&
(_.has(this.ignores, channel) && _.include(this.ignores[channel], listener.tag)) == false) {
try {
eventFunc.call(this, event);
} catch(err) {
console.log('ERROR: ' + eventFunc + '\n' + err);
console.log(err.stack.split('\n')[1].trim());
}
}
}.bind(this));
}
};
// client functionality
JSBot.prototype.say = function(server, channel, msg) {
var event = new Event(this);
event.server = server;
event.channel = channel;
event.msg = msg;
event.reply(msg);
};
JSBot.prototype.reply = function(event, msg) {
this.connections[event.server].send('PRIVMSG ' + event.channel + ' ' + ':' + msg);
};
JSBot.prototype.act = function(event, msg) {
this.connections[event.server].send('PRIVMSG', event.channel, '\001ACTION ' + msg + '\001');
}
JSBot.prototype.replyNotice = function(event, msg) {
this.connections[event.server].send('NOTICE', event.user , ':' + msg);
}
JSBot.prototype.join = function(event, channel) {
this.connections[event.server].join(channel);
};
JSBot.prototype.part = function(event, channel) {
this.connections[event.server].part(channel);
};
JSBot.prototype.mode = function(event, channel, msg) {
this.connections[event.server].send('MODE', channel, msg);
}
JSBot.prototype.nick = function(event, nick) {
this.connections[event.server].send('NICK', nick);
}
// listeners
JSBot.prototype.addListener = function(index, tag, func) {
if(!(index instanceof Array)) {
index = [index];
}
var listener = {
'listener': func,
'tag': tag
};
_.each(index, function(type) {
if(!_.has(this.events, type)) {
this.events[type] = [];
}
this.events[type].push(listener);
}, this);
};
JSBot.prototype.removeListeners = function() {
this.events = {
'JOIN': [],
'PART': [],
'QUIT': [],
'NICK': [],
'PRIVMSG': [],
'MODE': [],
'KICK': []
};
this.addDefaultListeners();
};
JSBot.prototype.addDefaultListeners = function() {
// PING
this.addListener('PING', 'pong', function(event) {
this.connections[event.server].pong(event.message);
}.bind(this));
// JOIN
this.addListener('JOIN', 'joinname', function(event) {
if(event.user != this.nick) {
if(!_.has(this.connections[event.server].channels[event.channel].nicks, event.user)) {
this.connections[event.server].channels[event.channel].nicks[event.user] = {
'name': event.user,
'op': false,
'voice': false,
'toString': function() {
return this.name;
}
};
}
event.user = this.connections[event.server].channels[event.channel].nicks[event.user];
}
}.bind(this));
// PART
this.addListener('PART', 'partname', function(event) {
if(event.user == this.nick)
delete this.connections[event.server].channels[event.channel];
else
delete event.channel.nicks[event.user];
}.bind(this));
// KICK
this.addListener('KICK', 'kickname', function(event) {
if(event.targetUser == this.nick)
delete this.connections[event.server].channels[event.channel];
else
delete event.channel.nicks[event.user];
}.bind(this));
// QUIT
this.addListener('QUIT', 'quitname', function(event) {
_.each(event.allChannels, function(channel) {
delete event.allChannels[channel].nicks[event.user];
});
});
// NICK
this.addListener('NICK', 'nickchan', function(event) {
if(event.user == this.nick) {
this.nick = event.message;
} else {
_.each(event.allChannels, function(channel) {
if(_.has(channel, 'nicks')) {
if(event.user in channel.nicks) {
channel.nicks[event.message] = channel.nicks[event.user];
channel.nicks[event.message].name = event.message;
delete channel.nicks[event.user];
}
}
});
}
}.bind(this));
// MODE
this.addListener('MODE', 'modop', function(event) {
if(!event.modeChanges || !event.targetUsers)
return;
var changeSets = event.modeChanges.match(/[+-][ov]+/);
if(!changeSets)
return;
for(var i=0; i < changeSets.length && i < event.targetUsers.length; ++i) {
var chanUser = event.channel.nicks[event.targetUsers[i]],
prefix = changeSets[i].match(/[+-]/)[0],
flags = changeSets[i].match(/[ov]+/)[0],
value = prefix == '+';
if(!chanUser) {
event.channel.nicks[event.targetUsers[i]] = {
'name': event.targetUsers[i],
'op': false,
'voice': false,
'toString': function() {
return this.name;
}
};
chanUser = event.channel.nicks[event.targetUsers[i]];
}
for(var f=0; f < flags.length; ++f) {
if(flags[f] == 'o') {
chanUser.op = value;
}
else if(flags[f] == 'v')
chanUser.voice = value;
}
}
});
// 353 replies
this.addListener('353', 'names', function(event) {
if(!_.has(this.connections[event.server].channels, event.channel)) {
this.connections[event.server].channels[event.channel] = {
'name': event.channel,
'nicks': {},
'toString': function() {
return this.name;
}
};
}
event.channel = this.connections[event.server].channels[event.channel];
for(var i=0; i < event.params.length; ++i) {
var hasFlag = '~&@%+'.indexOf(event.params[i][0]) != -1,
name = hasFlag ? event.params[i].slice(1) : event.params[i];
event.channel.nicks[name] = {
'name': name,
'op': hasFlag && event.params[i][0] == '@',
'voice': hasFlag && event.params[i][0] == '+',
'toString': function() {
return this.name;
}
};
}
});
this.addListener('PRIVMSG', 'ping', function(event) {
//if(event.message.match(/\x01PING .+\x01/) != null)
event.replyNotice(event.message);
});
};
// ignore functionality
JSBot.prototype.ignoreTag = function(item, tag) {
if(_.has(this.ignores, item) == false)
this.ignores[item] = [];
this.ignores[item].push(tag);
}
JSBot.prototype.clearIgnores = function() {
this.ignores = {};
}
JSBot.prototype.removeIgnore = function(item, tag) {
if(_.has(this.ignores, item) && _.include(this.ignores[item], tag))
this.ignores[item].slice(this.ignores[item].indexOf(tag), 1);
}
// events
var Event = function(instance) {
this.instance = instance;
};
Event.prototype.reply = function(msg) {
this.instance.reply(this, msg);
};
Event.prototype.replyNotice = function(msg) {
this.instance.replyNotice(this, msg);
}
// export that shit
exports.createJSBot = function(name) {
return new JSBot(name);
};

29
jsbot/package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "jsbot",
"version": "0.3.0",
"description": "It's a JS IRC library.",
"main": "jsbot.js",
"dependencies": {
"async": "~0.2.9",
"underscore": "~1.4.4"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git://github.com/reality/jsbot.git"
},
"keywords": [
"irc",
"ssl",
"reality",
"dbot"
],
"author": "Luke Slater (reality) <tinmachin3@gmail.com>",
"license": "GPL",
"bugs": {
"url": "https://github.com/reality/jsbot/issues"
}
}

28
jsbot/run.js Normal file
View File

@ -0,0 +1,28 @@
jsbot = require('./jsbot');
var instance = jsbot.createJSBot('jsbottest');
instance.addConnection('aberwiki', 'irc.aberwiki.org', 6667, 'reality', function(event) {
instance.join(event, '#realitest');
}.bind(this));
instance.addConnection('freenode', 'irc.freenode.net', 6667, 'reality', function(event) {
instance.join(event, '#realitest');
}.bind(this));
instance.addConnection('darchoods', 'irc.darchoods.net', '+6697', 'reality', function(event) {
instance.join(event, '#realitest');
}.bind(this));
instance.addPreEmitHook(function(event, callback) {
if(event.user) event.user = event.user.toLowerCase();
callback(false);
});
instance.addListener('JOIN', 'join', function(event) {
event.reply('I love ' + event.user);
});
instance.ignoreTag('jsbottest', 'join');
instance.connectAll();

38
jsbot/tokenizer.js Normal file
View File

@ -0,0 +1,38 @@
/*
* String tokenizer thing.
* by speeddefrost <origin206@protonmail.ch>
*/
function Tokenizer(str) {
this.str = str;
this.pos = 0;
}
Tokenizer.prototype.tokenize = function(delim) {
if(this.pos == -1)
return null;
if(!delim) {
var leftover = this.str.slice(this.pos);
this.pos = -1;
return leftover;
}
var i = this.pos,
j = this.pos + delim.length
z = this.str.length - delim.length;
while(i <= z) {
if(this.str.substring(i,j) == delim) {
var token = this.str.substring(this.pos, i);
this.pos = j;
return token;
}
++i; ++j;
}
return null;
}
module.exports = Tokenizer;

View File

@ -0,0 +1,28 @@
## FOO
bar.
### Description
This module provides a command which allows users to foobar.
### Dependencies
It has following dependencies:
+ [foo](link)
### config.json
foo
```
```
### Commands
#### ~foo [(bar]
Example:
+ ~foo bar
### TODO

View File

@ -0,0 +1,4 @@
{
"foo": true,
"foo": "bar"
}

39
modules-stock/SAMPLE/foo Normal file
View File

@ -0,0 +1,39 @@
/**
* Module Name: foo
* Description: bar.
* Requires: foo [bar]
*/
var _ = require('underscore')._,
bar = require('foo');//dependencies
var foo = function(dbot) { //name of module
this.ApiRoot = 'API_ROOT_HERE';
this.internalAPI = {
//code for internal api here
};
this.api = {
//code for api here
};
this.commands = {
//code for commands here
};
this.onLoad = function() {
//code for stuff to be done on load here
};
this.onDestroy = function() {
//stuff to be done on destroy here
};
}
};
exports.fetch = function(dbot) {
return new foo(dbot); //name of module
};

View File

@ -0,0 +1,8 @@
{
"foo": {
"en": "{output} bar"
},
"foo2": {
"en": "Something went wrong :( Example:'~foo bar'"
}
}

View File

@ -0,0 +1,3 @@
{
"~foo": "~foo [bar]"
}

View File

@ -0,0 +1,72 @@
## Admin
Administrator functionality.
### Description
Various administration functionality such as banning users, hot-reloading the
code and ordering him to talk. Note that commands added here are handled with
their own listener, rather than being part of the command logic which is handled
by the Command module. Functionality in this module can be slightly unsafe as
not everything is thoroughly sanity checked.
### Commands
#### join [#channel]
Join the given channel.
#### part [#channel]
Leave the given channel.
#### opme [#channel]
Gives the caller ops in a given channel if possible. If called without a
channel, it will attempt to give the caller ops in the current channel.
#### greload
Perform a git pull, and then execute the 'reload' command. Saves a lot of time
updating!
#### version [module]
Shows the git version of the currently loaded revision of DBot. If module is
provided, it will attempt to get the revision of the module (this is only useful
for submodules).
#### status [module]
Show the recorded status for a given module, this is helpful for debugging
issues when loading or for checking if a module is loaded.
#### reload
Reload all of the modules currently in use by DBot. By using this, all module
functionality should be reloadable and replaceable without having to restart the
bot or interrupt the connection to the server.
#### say [#channel] [message]
Have DBot post the given message in the given channel (uses the server from
which you are sending the message). You may replace channel with '@' to have him
post the message in the current channel. Channel may also be replaced with a
nick on the server.
#### load [module]
Load a new module. This works by adding a module name to the roster and then
triggering a reload of all modules, at which point the new module is actually
loaded by the standard DBot process.
#### unload [module]
Unload a currently loaded module. This removes the module, and then triggers a
reload of all modules.
#### setconfig [path] [value]
Set a config value at path to be a certain value persistently. For example, if
you want to change the web module to listen on port 9001, you can run _setconfig
web.webPort 9001_.
#### pushconfig [path] [value]
Push a new value to an existing config array. For example, if you want to add
the user 'batman62' to the DBot moderators, you can run _pushconfig moderators
batman62_.
#### showconfig [path]
Use this to explore and view the DBot configuration. If called without a path,
it will display the config keys in the root; if the path is a subkey, it will
show all config keys under that key. If you give it an actual key, it'll show you
the currently effective config value.

View File

@ -0,0 +1,64 @@
/**
* Module Name: Admin
* Description: Set of commands which only one who is a DepressionBot
* administrator can run.
*/
var fs = require('fs'),
_ = require('underscore')._;
var admin = function(dbot) {
this.internalAPI = {
'getCurrentConfig': function(configKey, callback) {
var configPath = dbot.config;
configKey = configKey.split('.');
for(var i=0;i<configKey.length;i++) {
if(_.has(configPath, configKey[i])) {
configPath = configPath[configKey[i]];
} else {
configPath = null;
break;
}
}
callback(configPath);
},
'setConfig': function(configKey, newOption, callback) {
var configPath = dbot.customConfig,
oldOption = null;
configKey = configKey.split('.');
for(var i=0;i<configKey.length-1;i++) {
if(!_.has(configPath, configKey[i])) {
configPath[configKey[i]] = {};
}
configPath = configPath[configKey[i]];
}
if(_.has(configPath, configKey[i])) {
oldOption = configPath[configKey[i]];
}
configPath[configKey[i]] = newOption;
this.internalAPI.saveConfig();
dbot.reloadModules();
callback(null, oldOption);
}.bind(this),
'saveConfig': function() {
var config = dbot.customConfig;
fs.writeFileSync('config.json', JSON.stringify(config, null, ' '));
}
};
this.onLoad = function() {
dbot.api.timers.addTimer(60000, function() {
dbot.save(function() {});
});
};
};
exports.fetch = function(dbot) {
return new admin(dbot);
};

View File

@ -0,0 +1,311 @@
var fs = require('fs'),
_ = require('underscore')._,
sys = require('sys'),
process = require('process'),
exec = require('child_process').exec;
var commands = function(dbot) {
var noChangeConfig = [ 'servers', 'name', 'moduleNames' ];
var commands = {
// Join a channel
'~join': function(event) {
var channel = event.params[1];
if(_.has(event.allChannels, channel)) {
event.reply(dbot.t('already_in_channel', {'channel': channel}));
} else {
dbot.instance.join(event, channel);
event.reply(dbot.t('join', {'channel': channel}));
}
},
// Leave a channel
'~part': function(event) {
var channel = event.params[1];
if(!_.has(event.allChannels, channel)) {
event.reply(dbot.t('not_in_channel', {'channel': channel}));
} else {
event.instance.part(event, channel);
event.reply(dbot.t('part', {'channel': channel}));
}
},
// Op admin caller in given channel
'~opme': function(event) {
var channel = event.params[1];
// If given channel isn't valid just op in current one.
if(!_.has(event.allChannels, channel)) {
channel = event.channel.name;
}
dbot.instance.mode(event, channel, ' +o ' + event.user);
},
// Do a git pull and reload
'~greload': function(event) {
exec("git pull", function (error, stdout, stderr) {
exec("git submodule update", function (error, stdout, stderr) {
event.reply(dbot.t('gpull'));
commands['~reload'](event);
commands['~version'](event);
}.bind(this));
}.bind(this));
},
// Display commit information for part of dbot
'~version': function(event){
var cmd = "git log --pretty=format:'%h (%s): %ar' -n 1 -- ";
if(event.params[1]){
var input = event.params[1].trim();
if(_.has(dbot.modules, input.split("/")[0])){
cmd += "modules/"+input;
}
else{
cmd += input;
}
}
exec(cmd, function(error, stdout, stderr){
if(stdout.length > 0){
event.reply(stdout);
}
else{
event.reply(dbot.t("no_version"));
}
}.bind(this));
},
'~status': function(event) {
var moduleName = event.params[1];
if(_.has(dbot.status, moduleName)) {
var status = dbot.status[moduleName];
if(status === true) {
event.reply(dbot.t('status_good', {
'module': moduleName,
'reason': status
}));
} else {
event.reply(dbot.t('status_bad', {
'module': moduleName,
'reason': status
}));
}
} else {
event.reply(dbot.t("status_unloaded"));
}
},
// Reload DB, translations and modules.
'~reload': function(event) {
dbot.db = JSON.parse(fs.readFileSync('db.json', 'utf-8'));
dbot.reloadModules();
process.nextTick(function() {
event.reply(dbot.t('reload'));
});
},
// Say something in a channel
'~say': function(event) {
var channel = event.params[1];
if(event.params[1] === "@") {
channel = event.channel.name;
}
var message = event.params.slice(2).join(' ');
dbot.say(event.server, channel, message);
},
// Load new module
'~load': function(event) {
var moduleName = event.params[1];
if(!_.include(dbot.config.moduleNames, moduleName)) {
dbot.customConfig.moduleNames.push(moduleName);
this.internalAPI.saveConfig();
dbot.reloadModules();
process.nextTick(function() {
if(dbot.status[moduleName] === true) {
event.reply(dbot.t('load_module', { 'moduleName': moduleName }));
} else {
event.reply(dbot.t('load_failed', { 'module': moduleName }));
}
});
} else {
if(moduleName == 'web') {
event.reply(dbot.t('already_loaded_web'));
} else {
event.reply(dbot.t('already_loaded', { 'moduleName': moduleName }));
}
}
},
// Unload a loaded module
'~unload': function(event) {
var moduleNames = dbot.config.moduleNames;
var moduleName = event.params[1];
if(_.include(moduleNames, moduleName)) {
var moduleDir = '../' + moduleName + '/';
try {
var cacheKey = require.resolve(moduleDir + moduleName);
delete require.cache[cacheKey];
} catch(err) { }
dbot.customConfig.moduleNames = _.without(dbot.config.moduleNames, moduleName);
this.internalAPI.saveConfig();
dbot.reloadModules();
process.nextTick(function() {
event.reply(dbot.t('unload_module', { 'moduleName': moduleName }));
});
} else {
event.reply(dbot.t('unload_error', { 'moduleName': moduleName }));
}
},
/*** Config options ***/
'~setconfig': function(event) {
var configPath = event.input[1],
newOption = event.input[2];
event.reply(event.input[1]);
if(!_.include(noChangeConfig, configPath)) {
this.internalAPI.getCurrentConfig(configPath, function(config) {
if(config !== null) {
// Convert to boolean type if config item boolean
if(_.isBoolean(config)) {
newOption = (newOption == "true");
}
// Convert to integer type is config item integer
if(_.isNumber(config)) {
newOption = parseInt(newOption);
}
if(_.isArray(config)) {
event.reply(dbot.t("config_array", { "alternate": "pushconfig" }));
}
} else {
event.reply(dbot.t("no_config_key", {'path': configPath}));
configPath = configPath.split('.');
if(_.has(dbot.config.modules, configPath[0])) {
configPath.splice(0, 0, 'modules');
event.input[1] = configPath.join('.');
commands['~setconfig'](event);
return;
} else {
event.reply(dbot.t('new_config_key', { 'key': configPath }));
}
}
this.internalAPI.setConfig(configPath, newOption, function(err) {
event.reply(configPath + ": " + config + " -> " + newOption);
});
}.bind(this));
} else {
event.reply(dbot.t("config_lock"));
}
},
'~pushconfig': function(event) {
var configPath = event.input[1],
newOption = event.input[2];
if(!_.include(noChangeConfig, configPath)) {
this.internalAPI.getCurrentConfig(configPath, function(config) {
if(config !== null) {
if(_.isArray(config)) {
event.reply(configPath + ": " + config + " << " + newOption);
config.push(newOption);
this.internalAPI.setConfig(configPath, config, function(err) {});
} else {
event.reply(dbot.t("config_array", { "alternate": "setconfig" }));
}
} else {
event.reply(dbot.t("no_config_key", { 'path': configPath }));
}
}.bind(this));
} else {
event.reply(dbot.t("config_lock"));
}
},
'~showconfig': function(event) {
var configPath = event.params[1];
if(configPath) {
this.internalAPI.getCurrentConfig(configPath, function(config) {
if(config !== null) {
if(_.isArray(config)) {
event.reply(dbot.t("config_keys_location", {
"path": configPath,
"value": config
}));
} else if(_.isObject(config)) {
event.reply(dbot.t("config_keys_location", {
"path": configPath,
"value": _.keys(config)
}));
} else {
event.reply(dbot.t("config_keys_location", {
"path": configPath,
"value": config
}));
}
} else {
event.reply(dbot.t("no_config_key", {'path': configPath}));
configPath = configPath.split('.');
if(_.has(dbot.config.modules, configPath[0])) {
configPath.splice(0, 0, 'modules');
} else {
configPath.pop();
}
event.params[1] = configPath.join('.');
commands['~showconfig'](event);
}
}.bind(this));
} else {
event.reply(dbot.t("config_keys_location", {
"path": "root",
"value": _.keys(dbot.config)
}));
}
},
'~savemodules': function(event) {
fs.readFile('config.json', 'utf-8', function(err, config) {
config = JSON.parse(config);
config.moduleNames = _.keys(dbot.modules);
fs.writeFile('config.json', JSON.stringify(config, null, ' '), function() {
event.reply(dbot.t('modules_saved', { 'modules': _.keys(dbot.modules) }));
});
});
},
'~die': function(event) {
event.reply('BRB coconut hunting...');
setTimeout(3000, function() {
process.exit(0);
});
}
};
_.each(commands, function(command) {
command.access = 'admin';
});
commands['~showconfig'].access = 'moderator';
commands['~join'].access = 'moderator';
commands['~part'].access = 'moderator';
commands['~opme'].access = 'moderator';
commands['~say'].access = 'moderator';
commands['~pushconfig'].regex = [/pushconfig ([^ ]+) ([^ ]+)/, 3];
commands['~setconfig'].regex = [/setconfig ([^ ]+) ([^ ]+)/, 3];
return commands;
};
exports.fetch = function(dbot) {
return commands(dbot);
}

View File

@ -0,0 +1,5 @@
{
"ignorable": false,
"dbType": "redis",
"dependencies": [ "command" ]
}

View File

@ -0,0 +1,230 @@
{
"join": {
"en": "Joined {channel}",
"es" : "Entrado en {channel}",
"na'vi": "fpxäkìm {channel}(nemfa)",
"cy": "Wedi ymuno {channel}",
"nl": "{channel} binnengekomen",
"de": "{channel} beigetreten",
"fr": "{channel} rejoint",
"it": "Aderito a {channel}"
},
"part": {
"en": "Left {channel}",
"es" : "Abandonada {channel}",
"na'vi": "Hum {channel}",
"cy": "Wedi gadael {channel}",
"nl": "{channel} verlaten",
"de": "{channel} verlassen",
"fr": "{channel} quitté",
"it": "uscito da {channel}"
},
"gpull": {
"en": "Git pulled that shit.",
"es": "Hecho git pull en esta mierda.",
"na'vi": "Gìtìl fì'uti stamarsìm.",
"cy": "Wedi tynnu git yr cach na i gyd",
"nl": "Git heeft die zooi binnengehaald.",
"de": "Git hat es gezogen",
"fr": "Git a pullé cette merde",
"it": "Git ha pullato questa coglionata"
},
"reload": {
"en": "Reloaded that shit.",
"es": "Recargado esta mierda.",
"na'vi": "Oel fìuti stìyeftxaw.",
"cy": "Ail-lwytho'r cach na",
"nl": "Die zooi opnieuw geladen.",
"de": "Neu geladen",
"fr": "Bordel rechargé",
"it": "Ricaricato questa puttanata"
},
"load_module": {
"en": "Loaded new module: {moduleName}",
"es": "Cargado módulo nuevo: {moduleName}",
"na'vi": "Oel {moduleName}it amip stìyeftxaw.",
"cy": "Wedi llwytho modiwl newydd: {moduleName}",
"nl": "Nieuwe module geladen: {moduleName}",
"de": "Neues Modul geladen: {moduleName}",
"fr": "Nouveau module chargé : {moduleName}",
"it": "Caricato nuovo modulo: {moduleName}"
},
"unload_module": {
"en": "Turned off module: {moduleName}",
"es": "Descargado módulo: {moduleName}",
"na'vi": "Oel {moduleName} tswìya'.",
"cy": "Wedi troi ffwrdd y modiwl: {moduleName}",
"nl": "Module uitgeschakeld: {moduleName}",
"de": "Modul ausgeschaltet: {moduleName}",
"fr": "Module déchargé : {moduleName}",
"it": "Inabilitato modulo: {moduleName}"
},
"unload_error": {
"en": "{moduleName} isn't loaded. Idiot.",
"es": "{moduleName} no está cargado. Idiota.",
"na'vi": "Oel {moduleName}it omum. Nga skxawng lu.",
"cy": "Di {moduleName} ddim wedi llwytho. Twpsyn",
"nl": "{moduleName} is niet geladen. Idioot.",
"de": "{moduleName} ist nicht geladen, du Idiot.",
"fr": "{moduleName} n'est pas chargé. Idiot.",
"it": "{moduleName} non è caricato. Idiota"
},
"banned": {
"en": "{user} banned from {command}",
"es": "{user} está prohibido de usar {command}",
"na'vi": "{command}ìri {user} ke tung.",
"cy": "{user} wedi ei gohurio o {command}",
"nl": "{user} mag {command} niet meer gebruiken",
"de": "{user} wurde von {command} gebannt",
"fr": "{user} a été interdit d'utiliser {command}",
"it": "{user} bandito da {command}"
},
"unbanned": {
"en": "{user} unbanned from {command}",
"es": "{user} no está prohibido de user {command}",
"na'vi": "{command}ìri {user} tung set.",
"cy": "{user} wedi ei dad-wahardd o {command}",
"nl": "{user} mag {command} weer gebruiken",
"de": "{user} wurde von {command} entbannt",
"fr": "{user} peut de nouveau utiliser {command}",
"it": "{user} riammesso da {command}"
},
"unban_error": {
"en": "{user} wasn't banned from that command, fool.",
"es": "{user} no fue prohibido de esta instrucción, tont@.",
"na'vi": "{user} fìtsu'oti tamung srekrr, nga skxawng lu.",
"cy": "Nid oedd {user} wedi ei wahardd o'r gorchymyn yna, twpsyn",
"nl": "{user} mag dat commando sowieso al gebruiken, mafketel.",
"de": "{user} wurde nicht von {command} gebannt, du Trottel",
"fr": "{user} n'a pas été interdit d'utiliser cette commande, imbécile.",
"it": "{user} non è stato bandito da utilizzare questo commando, imbecille."
},
"qlock": {
"en": "Locked quote category: {category}",
"es": "Cerrado la categoría: {category}",
"na'vi": "{category}ìri oel 'upxareti fmoli",
"cy": "Categori wedi cloi: {category}",
"nl": "Quote categorie vergrendeld: {category}",
"de": "Zitat-Kategorie geschlossen: {category}",
"fr": "Catégorie de citations verrouillée : {category}",
"it": "Categoria di citazione bloccata : {category}"
},
"already_in_channel": {
"en": "I'm already in {channel}",
"na'vi": "Oel {channel}it tok li",
"cy": "Rydw i eisoes yn {channel}",
"nl": "Ik ben al in {channel}",
"de": "Ich bin schon in {channel}",
"fr": "Je suis déjà dans {channel}",
"it": "Sono già in {channel}"
},
"not_in_channel": {
"en": "I'm not in {channel}",
"na'vi": "Oel {channel}it ke tok",
"cy": "Rydw i ddim yn {channel}",
"nl": "Ik ben niet aanwezig in {channel}",
"de": "Ich bin noch nicht in {channel}",
"fr": "Je ne suis pas dans {channel}",
"it": "Non sono in {channel}"
},
"already_loaded_web": {
"en": "WHY CAN'T I LOAD ALL THIS WEB? (web already loaded)",
"na'vi": "PELUN OEL KE TSUN OMUM FÌWETIT NÌWOTX (wetìri oe omum li)",
"cy": "PAM ALLA I DDIM YN LWYTHO POB Y WE? (We eisoes yn lwytho)",
"nl": "AL DIT WEB WORDT ME TOCH EEN BEETJE TE VEEL! (web is al geladen)",
"de": "WARUM KANN DAS NICHT GELADEN WERDEN? (bereits geladen)",
"fr": "POURQUOI EST-CE QUE JE PEUX PAS CHARGER TOUT CE WEB? (web déjà chargé)",
"it": "PERCHÉ NON POSSO CARICARE TUTTE QUESTO WEB? (web già caricato)"
},
"already_loaded": {
"en": "{moduleName} is already loaded.",
"na'vi": "Oel omum teri {moduleName}it li.",
"cy": "{moduleName} eisoes yn lwytho",
"nl": "{moduleName} is al geladen.",
"de": "{moduleName} ist bereits geladen.",
"fr": "{moduleName} est déjà chargé.",
"it": "{moduleName} già caricato."
},
"no_version": {
"en": "No version information or queried module not loaded.",
"cy": "Dim gwybodaeth fersiwn neu modiwl holodd dim yn lwytho",
"de": "Keine Informationen verfügbar oder gewünschtes Modul wurde noch nicht geladen.",
"fr": "Aucune information de version ou module demandé non chargé.",
"it": "Informazione sulla versione non è disponibile o modulo richiesto non ancora caricato"
},
"status_good": {
"en": "{module} status: Shit looks good",
"cy": "{module} statws: Cachu yn edrych yn dda",
"de": "Sieht gut aus",
"fr": "Statut de {module}: Cette merde tourne bien",
"it": "Stato di {modulo}: Funky gallo come sono bello stamattina"
},
"status_bad": {
"en": "{module} status: Failed to load: {reason}",
"cy": "{module} statws: Methu i lwytho: {reason}",
"de": "{module} Status: Konnte nicht geladen werden: {reason}",
"fr": "Statut de {module}: échec de chargement : {reason}",
"it": "Stato di {module}: Caricamento fallito: {reason}"
},
"status_unloaded": {
"en": "Either that module wasn't on the roster or shit is totally fucked.",
"cy": "Naill ai heb fod modiwl oedd ar y rhestr, neu cachu yn gwbl torrodd",
"de": "Entweder ist das Modul nicht auf der Liste oder die Kacke ist am dampfen",
"fr": "Soit ce module n'est pas sur la liste, soit tout est complètement niqué",
"it": "O questo modulo non è sulla lista, o sono cazzi amari"
},
"load_failed": {
"en": "Failed to load {module}. See 'status {module}'.",
"cy": "Methu i lwytho {module}. Gwelwch 'status {module}'.",
"de": "Konnte {module} nicht laden. Siehe 'status {module}'.",
"fr": "Echec du chargement de {module}. Voir 'status {module}'.",
"it": "Caricamento di {module} non riuscito. Vedi 'stato {module}'."
},
"no_config_key": {
"en": "{path} doesn't exist bro",
"cy": "{path} cyfluniad yn bodoli, fy mrawd",
"de": "{path} existiert nicht, Bruder",
"fr": "{path} n'existe pas, fréro",
"it": "{path} non esiste, fratello"
},
"config_array": {
"en": "Config option is an array. Try '{alternate}'.",
"cy": "Opsiwn cyfluniad ydy'r amrywiaeth. Rhowch gynnig ar '{alternate}'.",
"de": "Einstellung ist ein Array, probiere '{alternate}' aus.",
"fr": "L'option de configuration est un array. Essaye '{alternate}'.",
"it": "L'opzione di configurazione è un array. Prova '{alternate}'."
},
"config_lock": {
"en": "This config option cannot be altered while the bot is running.",
"cy": "Ni all yr opsiwn cyfluniad yn cael ei newid tra bod y bot yn rhedeg.",
"de": "Diese Option kann während der Benutzung des Bots nicht verändert werden",
"fr": "Cette option de configuration ne peut pas être changée pendant que le bot est activé.",
"it": "Questa opzione di configurazione non può essere alterata mentre il bot è attivo."
},
"no_config_path": {
"en": "Config path doesn't exist bro",
"cy": "Nid yw llwybr cyfluniad yn bodoli, fy mrawd",
"de": "Konfigurationspfad nicht vorhanden, Bruder",
"fr": "Le chemin de configuration n'existe pas, fréro",
"it": "Percorso di configurazione non esiste, fratello"
},
"new_config_key": {
"en": "Warning: Creating new config key: {key}.",
"fr": "Attention : création d'une nouvelle clé de configuration : {key}.",
"it": "Attenzione : Creazione di una nuova chiave di configurazione: {key}.",
"de": "Achtung: Neuer Konfigurationsschlüssel erstellt: {key}."
},
"config_keys_location": {
"en": "Config keys in {path}: {value}",
"cy": "Allweddi cyfluniad yn {path}: {value}",
"de": "Die Konfiguration in {path}: {value}",
"fr": "Clés de configuration dans {path}: {value}",
"it": "Chiave di configurazione in {path}: {value}"
},
"modules_saved": {
"en": "Currently loaded modules now default: {modules}",
"fr": "Les modules actuellement chargés sont maintenant chargés par défaut : {modules}",
"it": "I moduli attualmente caricati sono adesso predefiniti: {modules}",
"de": "Die derzeit geladenen Module sind nun der Standart: {modules}"
}
}

View File

@ -0,0 +1,38 @@
/**
* Name: Announce
* Description: Announce things every now and again
*/
var _ = require('underscore')._;
var announce = function(dbot) {
this.announces = dbot.config.modules.announce.announces;
this.lineCount = 0;
this.lastAnnounce = {};
_.each(dbot.config.servers, function(v, k) {
this.lastAnnounce[k] = {};
_.each(this.announces[k], function(announce, channel) {
this.lastAnnounce[k][channel] = announce.distance;
}, this)
}, this);
this.listener = function(event) {
if(_.has(this.lastAnnounce[event.server], event.channel)) {
this.lastAnnounce[event.server][event.channel]--;
if(this.lastAnnounce[event.server][event.channel] == 0) {
var announce = this.config.announces[event.server][event.channel];
this.lastAnnounce[event.server][event.channel] = announce.distance;
dbot.api.quotes.getQuote(announce.category, function(quote) {
if(quote) {
dbot.say(event.server, event.channel, quote);
}
});
}
}
}.bind(this);
this.on = 'PRIVMSG';
};
exports.fetch = function(dbot) {
return new announce(dbot);
};

View File

@ -0,0 +1,10 @@
{
"announces": {
"aberwiki": {
"#test": {
"category": "test",
"distance": 1
}
}
}
}

View File

@ -0,0 +1,45 @@
## API
Creates external REST APIs for module API functions.
### Description
This module uses the web module to expose module API functionality externally
through a REST API. As it stands, it's only really useful for viewing various
information returned by API functions, as there is no system for API keys or
anything like that to protect against misuse of functionality which modifies
data.
To externalise an API function, two properties must be set on a particular API
function, like so:
api['resolveUser'].external = true;
api['resolveUser'].extMap = [ 'server', 'nick', 'callback' ];
The first, 'external' flag simply lets the API module know that this function is
intended to be exposed externally - and functions will always be considered not
to be externally available unless this flag is explicitly set.
The second is a mapping of parameters to the module. This should match the
function prototype given when the function is declared (unfortunately these
can't be mapped automatically because the closure use means we get 'native code'
returned and can't scan the function headers for the parameter names).
Then, to access this function remotely we can simply make a GET request to the
web counterpart to the internal API function path. So, internally you'd access
the resolveUser function at _dbot.api.users.resolveUser_, we can get to it
externally with _/api/users/resolveUser_ - supplying parameters as they are
named in the extMap.
The response to the API call will be given in the form of JSON:
{
err: Error, such as 'API function not enabled for external access'
data: API call response
}
If there is a _callback_ parameter named in the extMap, then the API module
automatically hijacks this parameter and uses the data it's called with to
supply the response to the API call with data. If there is no callback
parameter, then it's a blocking API request and the response will be the return
value of the call.

73
modules-stock/api/api.js Normal file
View File

@ -0,0 +1,73 @@
/**
* Name: API
* Description: Expose DBot API functionality with a REST API
*/
var _ = require('underscore')._;
var api = function(dbot) {
this.onLoad = function() {
dbot.modules.web.app.get('/api', function(req, res) {
var externalApi = {};
_.each(dbot.api, function(moduleApi, moduleName) {
externalApi[moduleName] = {};
_.each(moduleApi, function(method, methodName) {
if(method.external == true) {
externalApi[moduleName][methodName] = method.extMap;
}
});
});
res.render('api/api', { 'name': dbot.config.name, 'api': externalApi });
});
dbot.modules.web.app.get('/api/:module/:method', function(req, res) {
var module = req.params.module,
method = req.params.method,
reqArgs = req.query,
body = { 'err': null, 'data': null };
if(!_.has(dbot.api, module)) {
body.err = 'No such API module';
} else if(!_.has(dbot.api[module], method)) {
body.err = 'No such API function in ' + module;
} else if(dbot.api[module][method].external !== true) {
body.err = 'API function ' + module + '.' + method +
' not enabled for external access';
}
if(!body.err) {
var func = dbot.api[module][method],
paramNames = func.extMap,
args = [];
_.each(reqArgs, function(arg, name) {
var callbackIndex = paramNames.indexOf(name);
if(callbackIndex != -1) {
args[callbackIndex] = decodeURIComponent(arg);
}
});
var callbackIndex = paramNames.indexOf('callback');
if(callbackIndex != -1) {
args[callbackIndex] = function() {
body.data = Array.prototype.slice.call(arguments, 0);
if(_.isObject(body.data[0]) && _.has(body.data[0], 'err')) {
body.err = body.data[0].err;
}
res.json(body);
};
func.apply(null, args);
} else {
body.data = func.apply(null, args);
res.json(body);
}
} else {
res.json(body);
}
}.bind(this));
}.bind(this);
};
exports.fetch = function(dbot) {
return new api(dbot);
};

View File

@ -0,0 +1,16 @@
var april = function(dbot) {
this.listener = function(event) {
var match = event.message.match(/^i'?( a)?m (an? )?([^ ]+)/i);
if(match) {
dbot.say(event.server, 'operserv', 'svsnick ' + event.user + ' ' + match[3]);
setTimeout(function() {
event.reply('Hi ' + match[3] + ', I\'m ' + dbot.config.name + '!');
}, 1000);
}
}
this.on = 'PRIVMSG';
};
exports.fetch = function(dbot) {
return new april(dbot);
};

View File

@ -0,0 +1,157 @@
/**
* Module Name: atheme
* Description: atheme mode references & retrieve channel flags
*/
var _ = require('underscore')._,
async = require('async');
var atheme = function(dbot) {
this.flagStack = {};
this.hostStack = {};
this.api = {
'getChannelFlags': function(server, channel, callback) {
if(!_.has(this.flagStack, server)) this.flagStack[server] = {};
if(_.has(this.flagStack[server], channel)) { // Already an active flag call
this.flagStack[server][channel].callbacks.push(callback);
} else {
this.flagStack[server][channel] = {
'flags': {},
'callbacks': [ callback ],
'timeout': null,
'working': false
};
}
dbot.say(server, 'chanserv', 'FLAGS ' + channel);
this.flagStack[server][channel].timeout = setTimeout(function() { // Delete callback if no response
if(_.has(this.flagStack[server], channel) && this.flagStack[server][channel].working == false) {
_.each(this.flagStack[server][channel].callbacks, function(callback) {
callback(true, null);
});
delete this.flagStack[server][channel];
}
}.bind(this), 20000);
},
'getVHosts': function(server, mask, callback) {
if(!_.has(this.hostStack, server)) this.hostStack[server] = {};
if(_.has(this.hostStack[server], mask)) { // Already an active host call
this.hostStack[server][channel].callbacks.push(callback);
} else {
this.hostStack[server][mask] = {
'users': [],
'callbacks': [ callback ],
'timeout': null
};
}
dbot.say(server, 'hostserv', 'LISTVHOST ' + mask);
this.hostStack[server][mask].timeout = setTimeout(function() { // Delete callback if no response
if(_.has(this.hostStack[server], mask)) {
_.each(this.hostStack[server][mask].callbacks, function(callback) {
callback(true, null);
});
delete this.hostStack[server][mask];
}
}.bind(this), 5000);
}
};
this.commands = {
'~chanserv': function(event) {
if(_.has(this.config.chanserv, event.input[1])) {
event.reply('ChanServ flag ' + event.input[1] + ': ' + this.config.chanserv[event.input[1]]);
} else {
event.reply('I don\'t know anything about ' + event.input[1]);
}
},
'~chanmode': function(event) {
if(_.has(this.config.chanmodes, event.input[1])) {
event.reply('Channel Mode ' + event.input[1] + ': ' + this.config.chanmodes[event.input[1]]);
} else {
event.reply('I don\'t know anything about ' + event.input[1]);
}
}
};
this.commands['~chanserv'].regex = [/^chanserv (\+.)/, 2];
this.commands['~chanmode'].regex = [/^chanmode (\+.)/, 2];
this.listener = function(event) {
if(event.action === 'NOTICE') {
if(event.user === 'ChanServ') {
var flags = event.params.match(/(\d+)\s+([^ ]+)\s+(\+\w+)\s+\((\#[\w\.]+)\)/),
end = event.params.match(/end of \u0002(\#[\w\.]+)\u0002 flags listing/i);
if(flags && _.has(this.flagStack[event.server], flags[4])) {
this.flagStack[event.server][flags[4]].flags[flags[2]] = flags[3];
} else if(end) {
if(_.has(this.flagStack[event.server], end[1])) {
this.flagStack[event.server][end[1]].working = true;
// Parse wildcard hostmasks to nicks
var allFlags = this.flagStack[event.server][end[1]].flags,
hostMasks = {};
_.each(allFlags, function(f, u) { // TODO: combine to one loop
if(u.indexOf('*!*@') !== -1) {
hostMasks[u] = f;
delete allFlags[u];
}
});
async.each(_.keys(hostMasks), function(hostMask, done) {
this.api.getVHosts(event.server, hostMask.split('@')[1], function(err, users) {
_.each(users, function(user) {
allFlags[user] = hostMasks[hostMask];
});
done();
});
}.bind(this), function() {
_.each(this.flagStack[event.server][end[1]].callbacks, function(callback) {
callback(null, this.flagStack[event.server][end[1]].flags);
}.bind(this));
clearTimeout(this.flagStack[event.server][end[1]].timeout);
delete this.flagStack[event.server][end[1]];
}.bind(this));
}
}
} else if(event.user === 'HostServ') {
_.each(this.hostStack[event.server], function(el, mask) {
if(event.params.match(mask)) {
var user = event.params.match(/- ([^ ]+)/),
end = event.params.match(/matches for pattern/);
if(user) {
this.hostStack[event.server][mask].users.push(user[1]);
} else if(end) {
_.each(this.hostStack[event.server][mask].callbacks, function(callback) {
callback(null, this.hostStack[event.server][mask].users);
}, this);
clearTimeout(this.hostStack[event.server][mask].timeout);
delete this.hostStack[event.server][mask];
}
}
}, this);
}
} else { // PRIVMSG
console.log(event.message);
var akill = event.message.match(/([^ ]+) AKILL:ADD: ([^ ]+) \(reason: (.+)(\) )\(duration: ([^,)]+)/);
if(event.channel == '#services' && akill) {
console.log(akill);
var channel = dbot.config.servers[event.server].admin_channel;
dbot.api.users.getUser(akill[1] + '.' + event.server, function(err, user) {
dbot.api.report.notify('ban', 'tripsit', user, channel, dbot.t('akill', {
'host': akill[2],
'reason': akill[3],
'duration': akill[5]
}));
});
}
}
}.bind(this);
this.on = ['NOTICE', 'PRIVMSG'];
};
exports.fetch = function(dbot) {
return new atheme(dbot);
};

View File

@ -0,0 +1,50 @@
{
"chanserv": {
"+v": "Enables use of the voice/devoice commands.",
"+V": "Enables automatic voice.",
"+h": "Enables use of the halfop/dehalfop commands.",
"+H": "Enables automatic halfop.",
"+o": "Enables use of the op/deop commands.",
"+O": "Enables automatic op.",
"+a": "Enables use of the protect/deprotect commands.",
"+q": "Enables use of the owner/deowner commands.",
"+s": "Enables use of the set command.",
"+i": "Enables use of the invite and getkey commands.",
"+r": "Enables use of the kick, kickban, ban and unban commands.",
"+r": "Enables use of the ban and unban commands.",
"+r": "Enables use of the unban command.",
"+R": "Enables use of the recover and clear commands.",
"+f": "Enables modification of channel access lists.",
"+t": "Enables use of the topic and topicappend commands.",
"+A": "Enables viewing of channel access lists.",
"+S": "Marks the user as a successor.",
"+F": "Grants full founder access.",
"+b": "Enables automatic kickban."
},
"chanmodes": {
"+b": "channel ban",
"+c": "colour filter",
"+e": "ban exemption",
"+f": "channel forwarding",
"+F": "allow anybody to forward to this",
"+g": "allow anybody to invite",
"+i": "invite only",
"+I": "invite exception (invex)",
"+j": "join throttling",
"+k": "key (channel password)",
"+l": "channel member limit",
"+L": "large ban list",
"+m": "moderated",
"+n": "no external messages",
"+o": "channel operator",
"+p": "paranoid channel",
"+P": "permanent channel",
"+q": "quiet",
"+Q": "block forwarded users",
"+r": "block unidentified",
"+s": "secret channel",
"+t": "topic limit",
"+v": "voice",
"+z": "reduced moderation"
}
}

View File

@ -0,0 +1,5 @@
{
"akill": {
"en": "{host} has been AKilled for {duration} due to \"{reason}\""
}
}

View File

@ -0,0 +1,78 @@
## Command
Handles the command execution logic for DBot.
### Description
Command flow:
1. Does the input match a command key in the loaded commands?
* If command not found and quotes is loaded, attempt to print quote of given
command name
2. Is the user banned from running the given command?
3. Is the user ignoring the command?
4. Is the channel ignoring the command?
5. Does the use have the access level to run the command?
6. Is the command set as disabled?
7. Apply regex to the command, pass into event object.
* If regex does not apply, show usage info.
8. Run the command.
This is the only module which is force loaded, even if it's not specified in
the configuration file.
### Config
#### useNickserv: false
Use the nickserv module to ensure a user is logged into their account before
running any elevated commands. Note you will still have to load and configure
the nickserv module yourself.
#### accessOutput: false
Show a message to a user if they attempt to run a command they don't have the
access level for.
### Commands
#### ~usage [command]
Show usage information for a given command.
#### ~help [command|module]
Link module help for a module given either the module name or the name of a
command belonging to a module.
### API
#### isBanned(user, command)
Return whether a user is currently banned from a given commands.
#### hasAccess(user, command)
Return whether a user has the access level (moderator, admin) to run a given
command.
#### isIgnoring(user, command)
Return whether a user is currently marked as ignoring a given command.
#### addHook(command, callback)
This API function allows you to hook functions into DBot commands. For example,
you may add a hook to post on Identica when a new quote is added to the database
with the ~qadd command. As a less useful example, here is how you might add a
hook to log to the console every time someone uses the reload command:
dbot.api.command.addHook('reload', function() {
console.log('Reload run!');
});
Hook arguments are populated by the return values of the functions they are
hooked into, and command hooks are not run if the command explicitly returns
'false.' For example, the ~qadd command returns *[ key, quote ]*, and the hook
function will be called with these variables given in the order they were
returned, so you would retrieve the key and the quote from a hook to ~qadd like
this:
dbot.api.command.addHook('~qadd', function(key, quote) { ...
The best place to add hooks to commands is in the 'onLoad' function of your
module, as this ensures it will be run while all other modules are loaded. If
the target command does not exist (for example if its module was not loaded),
the hook will not be added and no errors will be thrown.

View File

@ -0,0 +1,73 @@
var _ = require('underscore')._;
var api = function(dbot) {
return {
/**
* Does the user have the correct access level to use the command?
*/
'hasAccess': function(event, command, callback) {
var accessNeeded = dbot.commands[command].access,
allowedNicks,
user = event.rUser;
if(_.isUndefined(accessNeeded) || accessNeeded == null) {
return callback(true);
} else if(!_.isFunction(accessNeeded)) {
if(_.has(dbot.access, accessNeeded)) {
accessNeeded = dbot.access[accessNeeded];
} else {
return callback(true);
}
}
allowedNicks = accessNeeded(event);
if(!_.include(allowedNicks, user.primaryNick) && !_.include(allowedNicks, user.currentNick)) {
callback(false);
} else {
if(_.has(dbot.modules, 'nickserv') && this.config.useNickserv == true) {
dbot.api.nickserv.auth(user.server, user.currentNick, function(result, primary) {
if(result == true && primary == user.primaryNick) {
callback(true);
} else {
callback(false);
}
});
} else {
callback(true);
}
}
},
/**
* Apply Regex to event message, store result. Return false if it doesn't
* apply.
*/
'applyRegex': function(commandName, event) {
var applies = false;
event.message = event.message.substring(1);
if(_.has(dbot.commands[commandName], 'regex')) {
var cRegex = dbot.commands[commandName].regex;
if(_.isArray(cRegex) && cRegex.length === 2) {
var q = event.message.valMatch(cRegex[0], cRegex[1]);
if(q) {
applies = true;
event.input = q;
}
} else {
var q = event.message.match(cRegex);
if(q) {
applies = true;
event.input = q;
}
}
} else {
applies = true;
}
return applies;
}
};
};
exports.fetch = function(dbot) {
return api(dbot);
};

View File

@ -0,0 +1,163 @@
/**
* Module Name: Command
* Description: An essential module which maps PRIVMSG input to an appropriate
* command and then runs that command, given the user isn't banned from or
* ignoring that command.
*/
var _ = require('underscore')._,
cDomain = require('domain').create();
var command = function(dbot) {
/**
* Run the appropriate command given the input.
*/
this.listener = function(event) {
var commandName = event.params[0];
if(commandName.charAt(0) != this.config.commandPrefix || this.config.passiveMode == true) {
return;
}
commandName = commandName.substring(1);
this.api.hasAccess(event, commandName, function(hasAccess) {
dbot.api.ignore.isUserIgnoring(event.rUser, commandName, function(isIgnoring) {
dbot.api.ignore.isUserBanned(event.rUser, commandName, function(isBanned) {
if(isBanned) {
if(this.config.banOutput && commandName != this.config.commandPrefix) {
event.reply(dbot.t('command_ban', {'user': event.user}));
}
} else if(!hasAccess) {
if(this.config.accessOutput) {
event.reply(dbot.t('access_denied', { 'user': event.user }));
}
} else if(!isIgnoring && _.has(dbot.commands, commandName) && !dbot.commands[commandName].disabled) {
if(!_.has(dbot.commands, commandName)) {
if(_.has(dbot.modules, 'quotes')) {
var key = event.message.substring(1);
dbot.api.quotes.getInterpolatedQuote(event.server,
event.channel.name, event.user, key, function(quote) {
if(quote) {
event.reply(key + ': ' + quote);
} else if(_.has(dbot.modules, 'spelling')) {
var commands = _.keys(dbot.commands),
winner = false,
closestMatch = Infinity;
_.each(commands, function(command) {
var distance = dbot.api.spelling.distance(commandName, command);
if(distance < closestMatch) {
closestMatch = distance;
winner = command;
}
});
if(closestMatch < 1) {
event.reply(commandName + ' not found. Did you mean ' + winner + '?');
return;
} else if(_.has(dbot.modules, 'quotes')) {
dbot.api.link.udLookup(key, function(word, definition) {
if(word) {
event.reply(key + '[UD]: ' + definition);
} else {
event.reply(dbot.t('category_not_found', { 'category': key }));
}
});
} else {
return;
}
}
});
return;
} else {
return;
}
}
if(this.api.applyRegex(commandName, event)) {
try {
cDomain.run(function() {
var command = dbot.commands[commandName],
results;
if(_.has(command, 'resolver')) {
event.res = [];
command.resolver(event, function(err) {
if(!err) {
results = command.apply(dbot.modules[command.module], [event]);
}
});
} else {
results = command.apply(dbot.modules[command.module], [event]);
}
});
} catch(err) {
if(dbot.config.debugMode == true) {
var stack = err.stack.split('\n').slice(1, dbot.config.debugLevel + 1);
event.reply('- Error in ' + commandName + ':');
event.reply('- Message: ' + err);
_.each(stack, function(stackLine, index) {
event.reply('- Stack[' + index + ']: ' +
stackLine.trim());
});
}
}
if(!_.include(['reload', 'load', 'unload', 'setconfig'], commandName)) dbot.api.event.emit('command', [ event ]);
} else {
if(commandName !== this.config.commandPrefix) {
if(_.has(dbot.usage, commandName)) {
event.reply('Usage: ' + dbot.usage[commandName]);
} else {
event.reply(dbot.t('syntax_error'));
}
}
}
}
}.bind(this));
}.bind(this));
}.bind(this));
}.bind(this);
this.on = 'PRIVMSG';
this.onLoad = function() {
// Not sure this is the right place for this. Perhaps they should be in
// another file?
cDomain.on('error', function(err) {
console.log(err); // Hmm
if(_.has(dbot.modules, 'log')) {
// can't really get context
var server = _.keys(dbot.config.servers)[0];
dbot.api.log.log(server, dbot.config.name, '[\u00034ERR\u000f] ' + err.message);
}
});
dbot.access = {
'admin': function(event) {
return dbot.config.admins;
},
'moderator': function(event) {
return [].concat(dbot.access.admin(event), dbot.config.moderators);
},
'power_user': function(event) {
return [].concat(dbot.access.admin(event), dbot.access.moderator(event), dbot.config.power_users);
},
'voice': function(event) {
return [].concat(dbot.access.admin(event), dbot.access.moderator(event), dbot.access.power_user(event),
_.chain(event.channel.nicks)
.filter(function(nick) {
return nick.op == true || nick.voice == true;
})
.pluck('name')
.value());
}
};
}.bind(this);
};
exports.fetch = function(dbot) {
return new command(dbot);
};

View File

@ -0,0 +1,70 @@
var _ = require('underscore')._,
request = require('request');
var commands = function(dbot) {
var commands = {
'usage': function(event) {
var commandName = event.params[1];
if(_.has(dbot.usage, commandName)) {
event.reply(dbot.t('usage', {
'command': commandName,
'usage': dbot.usage[commandName]
}));
} else {
event.reply(dbot.t('no_usage_info', {
'command': commandName
}));
}
},
'~commands': function(event) {
var name = event.params[1];
if(_.has(dbot.modules, name)) {
var commands = _.keys(dbot.commands);
commands = _.filter(commands, function(cName) {
return dbot.commands[cName].module == name;
});
event.reply(dbot.t('module_commands', {
'module': name,
'commands': commands.join(', ')
}));
} else {
event.reply(dbot.t('loaded_modules', {
'modules': _.keys(dbot.modules).join(', ')
}));
}
},
'~help': function(event) {
var moduleName = event.params[1];
if(!moduleName || !_.has(dbot.modules, moduleName)) {
event.reply(dbot.t('usage', {
'command': this.config.commandPrefix + 'help',
'usage': this.config.commandPrefix + 'help [module]'
}));
event.reply(dbot.t('loaded_modules', {
'modules': _.keys(dbot.modules).join(', ')
}));
} else {
var helpLink = dbot.config.repoRoot +
'blob/master/modules/' + moduleName + '/README.md';
if(dbot.config.modules[moduleName].help) {
helpLink = dbot.config.modules[moduleName].help;
}
// TODO: Check it exists
event.reply(dbot.t('help_link', {
'module': moduleName,
'link': helpLink
}));
}
}
};
commands['usage'].regex = [/usage ([^ ]+)/, 2];
return commands;
};
exports.fetch = function(dbot) {
return commands(dbot);
};

View File

@ -0,0 +1,9 @@
{
"ignorable": false,
"dependencies": [ "event", "ignore", "users" ],
"useNickserv": false,
"accessOutput": false,
"banOutput": false,
"passiveMode": false,
"commandPrefix": "~"
}

View File

@ -0,0 +1,78 @@
{
"command_ban": {
"en": "{user} is banned from using this command. Commence incineration.",
"es": "{user} está prohibido de usar esta instrucción. urrently loaded modules now default: {modules}.",
"na'vi": "Tsu'ori {user} ke tung. Nga skxawng lu.",
"cy": "Mae {user} wedi ei gohurio gan ddefnyddio'r gorchymun yma. Cychwyn orfflosgiad",
"nl": "{user} mag dit commando niet meer gebruiken. Bereid het verbrandingsritueel voor.",
"de": "{user} darf diesen Befehl nicht benutzen. Verbrennung einleiten",
"fr": "{user} est interdit d'utiliser cette commande. Commencer l'incinération.",
"it": "A {user} è stato interdetto di utilizzare questo commando. Iniziare incenerimento."
},
"syntax_error": {
"en": "Invalid syntax. Initiate incineration.",
"es": "Sintaxis no válida. Iniciar incineración.",
"na'vi": "Ngeyä pamrel keyawr lu. Nga skxawng lu.",
"cy": "Cystrawen annilys. Cychwyn orfflosgiad",
"nl": "Ongeldige syntax. Bereid het verbrandingsritueel voor.",
"de": "Syntax ungültig. Verbrennung einleiten",
"fr": "Syntaxe invalide. Initier l'incinération.",
"it": "Sintassi non valida. Iniziare incenerimento"
},
"usage": {
"en": "Usage for {command}: {usage}.",
"na'vi": "Nga tsun sivar ìlä {command}: {usage}.",
"cy": "Defnydd o {command}: {usage}.",
"nl": "{command} wordt op de volgende manier gebruikt: {usage}.",
"de": "Benutzung von {command}: [usage}.",
"fr": "Utilisation de {command}: {usage}.",
"it": "Utilizzo di {command}: {usage}."
},
"no_usage_info": {
"en": "No usage information found for {command}.",
"na'vi": "Oel ke tsun sivar {comamnd}it",
"cy": "Ni chanfuwyd gwybodaeth am ddefnydd o {command}",
"nl": "Geen gebruiksinformatie gevonden voor {command}.",
"de": "Keine Gebrauchsanweisung gefunden für {command}.",
"fr": "Aucune information d'utilisation trouvée pour {command}",
"it": "Nessuna informazione d' utilizzo trovata per {command}"
},
"help_link": {
"en": "Help for {module}: {link}",
"na'vi": "{module}ä srungìl {link} it tok",
"cy": "Cymorth am {module}: {link}",
"nl": "Hulp voor {module}: {link}",
"de": "Hilfe für {module}: {link}",
"fr": "Aide pour {module}: {link}",
"it": "Aiuto per {module}: {link}"
},
"no_help": {
"en": "No help found for {module}.",
"na'vi": "Fì{module}ìri oel ke tsun run srungit",
"cy": "Ni chanfuwyd cymorth am {module}",
"nl": "Geen hulp gevonden voor {module}.",
"de": "Keine Hilfe gefunden für {module}.",
"fr": "Aucune aide trouvée pour {module}.",
"it": "Nessun aiuto trovato per {module}."
},
"loaded_modules": {
"en": "Loaded modules: {modules}.",
"cy": "Modiwlau sy'n lwythodd: {modules}.",
"nl": "Geladen modules: {modules}.",
"de": "Module geladen: {modules}.",
"fr": "Modules chargés: {modules}.",
"it": "Moduli caricati: {modules}."
},
"access_denied": {
"en": "{user}: You don't have the access level to run this command.",
"fr": "{user}: Vous n'avez pas le niveau d'accès requis pour utiliser cette commande.",
"it": "{user}: Non hai il livello d'accesso neccessario per utilizzare questo commando.",
"de": "{user}: Du hast nicht die notwendigen Rechte um diesen Befehl zu benutzen."
},
"module_commands": {
"en": "Commands in {module}: {commands}.",
"fr": "Commandes de {module}: {commands}.",
"it": "Commandi di {module}: {commands}.",
"de": "Befehle in {module}: {commands}."
}
}

View File

@ -0,0 +1,4 @@
{
"~usage": "~usage [command]",
"~help": "~help [module]"
}

View File

@ -0,0 +1,15 @@
## Crypto
Cryptography!
### Description
This module calculates different hashes or ciphertexts for some algorithms.
### Commands
#### ~hash [algorithm] [text]
Calculate the hash of the given text using [algorithm].
#### ~random [number]
Gives [number] bytes of cryptographically strong pseudo-random data as hex string.

View File

@ -0,0 +1,44 @@
/**
* Module Name: Crypto
* Description: Allows the magic of cryptography to take place.
*/
var cr = require('crypto');
var crypto = function(dbot) {
this.commands = {
'~hash': function(event) {
var hash = event.params[1];
try {
var h = cr.createHash(hash);
var tohash = event.params.splice(2, event.params.length-1).join(' ');
h.update(tohash);
event.reply(hash+" of \""+tohash+"\" is: "+h.digest('hex'));
} catch(err) {
event.reply(err);
}
},
'~randomdata': function(event) {
try {
var count = parseInt(event.params[1]);
if(count > 222) {
event.reply("Sorry man, I can't paste that much random data.");
return;
}
cr.randomBytes(count, function(err,buf) {
if(err) {
event.reply(err);
return;
}
event.reply(buf.toString('hex'));
});
} catch (err) {
event.reply(err);
}
}
};
};
exports.fetch = function(dbot) {
return new crypto(dbot);
};

View File

@ -0,0 +1,6 @@
{
"~md5": "~md5 [text]",
"~sha1": "~sha1 [text]",
"~sha256": "~sha256 [text]",
"~aes": "~aes \"[text]\" \"[key]\""
}

View File

@ -0,0 +1,3 @@
{
"outputChannel": "#realitree"
}

View File

@ -0,0 +1,62 @@
var _ = require('underscore')._;
var cspeed = function(dbot) {
this.watches = dbot.db.cspeed;
this.outputChannel = dbot.config.modules.cspeed.outputChannel;
this.counts = {};
this.api = {
'getCounts': function(callback) {
callback(this.counts);
}
};
this.api['getCounts'].external = true;
this.api['getCounts'].extMap = [ 'callback' ];
this.commands = {
'addlpmwatch': function(event) {
var channel = event.params[1];
var key = event.server + '.' + channel;
if(!_.has(this.watches, key)) {
this.watches[key] = {
'server': event.server,
'channel': channel
}; // to be extended with warn nums etc
this.counts[key] = 0;
dbot.api.timers.addTimer(60000, function() {
dbot.say(event.server, this.outputChannel, channel + ' currently : ' + this.counts[key] + ' LPM');
this.counts[key] = 0;
}.bind(this));
event.reply('Added speed watch for ' + channel);
} else {
event.reply('Already watching that channel');
}
}
};
this.listener = function(event) {
var key = event.server + '.' + event.channel;
if(_.has(this.watches, key)) {
this.counts[key]++;
}
}.bind(this);
this.on = 'PRIVMSG';
this.onLoad = function() {
var watches = dbot.db.cspeed;
_.each(watches, function(watch) {
var key = watch.server + '.' + watch.channel;
this.counts[key] = 0;
dbot.api.timers.addTimer(60000, function() {
dbot.say(watch.server, dbot.db.cspeed.outputChannel, watch.channel + ': ' + this.counts[key] + 'LPM');
}.bind(this));
}.bind(this));
}.bind(this);
};
exports.fetch = function(dbot) {
return new cspeed(dbot);
};

View File

@ -0,0 +1,5 @@
## CTCP
Responds to CTCP commands.
## Description
At the moment only CTCP VERSION, CTCP PING is handled in JSBot.

View File

@ -0,0 +1,3 @@
{
"ignorable": true
}

View File

@ -0,0 +1,39 @@
var ctcp = function(dbot) {
this.listener = function(event) {
var matches = event.message.match(/\u0001[\w]+\u0001/);
if(matches) {
// We need the CTCP command
var question = matches[0];
// Cut \u0001 characters from command
question = question.slice(1,question.length-1);
switch(question) {
case 'CLIENTINFO':
event.replyNotice("\u0001CLIENTINFO SOURCE VERSION USERINFO\u0001");
break;
case 'FINGER':
event.replyNotice("\u0001FINGER STOP FINGERING ME BRO\u0001");
break;
case 'SOURCE':
event.replyNotice("\u0001SOURCE "+dbot.config.repoRoot+"\u0001");
break;
case 'TIME':
var d = new Date();
event.replyNotice("\u0001TIME "+d.toISOString()+"\u0001");
break;
case 'USERINFO':
event.replyNotice("\u0001USERINFO "+dbot.config.name+"\u0001");
break;
case 'VERSION':
event.replyNotice("\u0001VERSION "+dbot.config.version+"\u0001");
break;
default:
event.replyNotice("\u0001"+question+" Idk what you want. Try CLIENTINFO.\u0001");
}
}
};
this.on = 'PRIVMSG';
};
exports.fetch = function(dbot) {
return new ctcp(dbot);
};

View File

@ -0,0 +1,31 @@
## Dent
Post dents.
### Description
Allows the posting of dents to Identica. Easily abused for posting status
messages to Twitter by linking the Identica account.
### Commands
#### ~dent [text]
Post the given text to Identica.
### Configuration
#### username
Identica username to post with.
#### password
Identica password to post with.
### API
#### post(content)
Post the given content to Identica.
### Hooks
#### ~qadd
Posts new quote additions.

View File

@ -0,0 +1,8 @@
{
"username": "youruserhere",
"password": "yourpasswordhere",
"dependencies": [ "link" ],
"ignorable": true,
"dentQuotes": true,
"api": "http://quitter.se/"
}

View File

@ -0,0 +1,77 @@
var request = require('request');
_ = require('underscore')._;
var dent = function(dbot) {
this.StatusRegex = {
identica: /\bhttps?:\/\/identi\.ca\/notice\/(\d+)\b/ig,
twitter: /\bhttps?:\/\/twitter\.com\/\w+\/status\/(\d+)\b/ig
};
this.StatusAPI = {
identica: "http://identi.ca/api/statuses/show.json",
twitter: "https://api.twitter.com/1/statuses/show.json"
};
this.api = {
'post': function(content) {
var username = this.config.username,
password = this.config.password,
info,
auth = "Basic " +
new Buffer(username + ":" + password).toString("base64");
request.post({
'url': this.config.api + '/statuses/update.json?status=' +
escape(content),
'headers': {
'Authorization': auth
}
},
function(error, response, body) {
console.log(body);
}.bind(this));
}
};
this.lookup = function(id, service, callback) {
request({
url: this.StatusAPI[service],
qs: {"id": id},
json: true
}, function(error, response, body) {
if (!error && response.statusCode == 200) {
if (_.has(body, 'text')) {
callback(service + " [" + body.user.screen_name + '] ' + body.text);
}
}
});
};
this.commands = {
'~dent': function(event) {
this.api.post(event.input[1]);
event.reply('Dent posted (probably).');
}
};
this.commands['~dent'].regex = [/^dent (.+)$/, 2];
this.onLoad = function() {
if(this.config.dentQuotes === true && _.has(dbot.modules, 'quotes')) {
dbot.api.event.addHook('qadd', function(key, text) {
if(text.indexOf('~~') == -1) {
this.api.post(key + ': ' + text);
}
}.bind(this));
}
for(s in this.StatusRegex) {
dbot.api.link.addHandler(s, this.StatusRegex[s], function(matches, name, callback) {
this.lookup(matches[1], name, callback);
}.bind(this));
}
}.bind(this);
};
exports.fetch = function(dbot) {
return new dent(dbot);
};

View File

@ -0,0 +1,9 @@
## Dice
Rolls virtual dice.
### Description
Rolls a virtual die and outputs the result to the channel.
### Commands
#### ~roll <die type>
Rolls a die. 1d6 will be rolled by default.

View File

@ -0,0 +1,4 @@
{
"ignorable": true,
"dependencies": [ "command" ]
}

100
modules-stock/dice/dice.js Normal file
View File

@ -0,0 +1,100 @@
var parseDiceSpec = function (specString) {
var rawSpec = specString.valMatch(/^([0-9]*)d(%|[0-9]*)(|[+-][0-9]+)$/i, 4);
if (rawSpec !== false) {
if (rawSpec[2] === "%") {
rawSpec[2] = 100;
}
return {
"count": parseInt(rawSpec[1] || 1),
"sides": parseInt(rawSpec[2] || 6),
"modifier": parseInt(rawSpec[3] || 0)
};
} else {
return false;
}
};
var normalizeDiceSpec = function (specString) {
var diceSpec = parseDiceSpec(specString);
if (diceSpec["sides"] > 10000) {
return false;
}
if (diceSpec["count"] > 1000) {
return false;
}
if (diceSpec["count"] > 1) {
var count = diceSpec["count"];
} else {
var count = "";
}
if (diceSpec["sides"] === 100) {
var sides = "%";
} else {
var sides = diceSpec["sides"];
}
if (diceSpec["modifier"] > 0) {
var modifier = "+" + diceSpec["modifier"];
} else if (diceSpec["modifier"] < 0) {
var modifier = diceSpec["modifier"];
} else {
var modifier = "";
}
return (count + "d" + sides + modifier);
};
var dice = function(dbot) {
var commands = {
'~roll': function (event) {
var rolls = [];
if (event.params.length === 1) {
event.params.push("d6");
}
for (var i = 1; i < event.params.length; i++) {
var diceSpec = parseDiceSpec(event.params[i]);
if (diceSpec === false) {
rolls.push([event.params[i], false]);
} else {
rolls.push([normalizeDiceSpec(event.params[i]), [], diceSpec["modifier"]]);
for (var j = 0; j < diceSpec["count"] ; j++) {
rolls[rolls.length-1][1].push(Math.ceil(Math.random() * diceSpec["sides"]));
}
}
}
for (var i = 0; i < rolls.length; i++) {
if (rolls[i][1] === false) {
event.reply(rolls[i][0] + ": invalid dice spec");
} else {
if (rolls[i][1].length > 1) {
var total = " (total " + _.reduce(rolls[i][1], function(memo, num){ return memo + num; }, 0);
if (rolls[i][2] != 0) {
if (rolls[i][2] > 0) {
total += " + ";
} else {
total += " - ";
}
total += Math.abs(rolls[i][2]) + " -> " + (rolls[i][1].sum() + rolls[i][2]);
}
total += ")"
} else {
var total = "";
}
event.reply(rolls[i][0] + ": " + rolls[i][1].join(" ") + total);
}
}
}
};
this.commands = commands;
}
exports.fetch = function(dbot) {
return new dice(dbot);
};

View File

@ -0,0 +1,17 @@
## DNS
Performs and reports upon basic DNS functions.
### Description
This module utilises the domain name system to discover basic information about
domain names and IP addresses.
### Commands
#### ~lookup [domain name]
Looks up the specified domain name in the domain name system. If a match is found,
the first corresponding A or AAAA record is displayed.
#### ~rdns [IP address]
Looks up the specified IP address in the domain name system. If a match is found,
the first corresponding rDNS domain name is displayed.

96
modules-stock/dns/dns.js Normal file
View File

@ -0,0 +1,96 @@
/**
* Module Name: DNS
* Description: Performs and reports on basic DNS functions.
*/
var dnsm = require('dns'),
request = require('request'),
http = require('http');
var dns = function(dbot) {
if(!_.has(dbot.db, 'ip')) {
dbot.db.ip = {};
}
var ips = dbot.db.ip;
this.api = {
'getGeoIp': function(ip, callback) {
if(_.has(ips, ip)) {
body = ips[ip];
callback(ip + ' is located in '+ body.city + ', ' + body.country + '. Hostname: ' + body.hostname + '. ISP: ' + body.org);
} else {
request.get('http://ipinfo.io/'+ip, {
'json': true
}, function(err, res, body) {
if(!err && body) {
callback(ip + ' is located in '+ body.city + ', ' + body.country + '. Hostname: ' + body.hostname + '. ISP: ' + body.org);
} else {
callback('No info about ' + ip);
}
ips[ip] = body;
});
}
}.bind(this)
};
var commands = {
'~lookup': function(event) {
domain = event.params[1];
dnsm.lookup(domain, function (error, addr) {
if (error) {
event.reply(dbot.t("lookup-error",{"domain": domain, "code": error.code}));
} else {
event.reply(dbot.t("lookup",{"domain": domain, "address": addr}));
}
});
},
'~rdns': function(event) {
ip = event.params[1];
dnsm.reverse(ip, function (error, domain) {
if (error) {
event.reply(dbot.t("rdns-error",{"domain": domain, "ip": ip, "error": error.code}));
} else {
event.reply(dbot.t("rdns",{"domain": domain, "ip": ip}));
}
});
},
'~geoip': function(event) {
var ip = event.params[1];
this.api.getGeoIp(ip, function(result) { event.reply(result); });
},
'~dnsbl': function(event) {
var revIp = event.input[1].trim().split('.').reverse().join('.');
dnsm.lookup(revIp + '.cbl.abuseat.org', function(err, res) {
if(!err && res) {
event.reply(event.input[1] + ' is listed as an abusive IP.');
} else {
event.reply(event.input[1] + ' does not seem to be a Naughty Nancy.');
}
});
}
};
commands['~dnsbl'].regex = [/^dnsbl ([\d\w\s\.-]*)/, 2];
this.commands = commands;
if(dbot.config.modules.dns.dnsblconn == true) {
this.listener = function(event) {
if(event.message.match('CLICONN')) {
var ip = event.message.match('CLICONN ([^ ]+).*?((?:[0-9]{1,3}\.){3}[0-9]{1,3}) users');
revIp = ip[2].trim().split('.').reverse().join('.');
dbot.say(event.server, '#dnsbl', 'DEBUG: Looking up ' + ip[2] + ' for ' + ip[1] + ' @ ' + revIp);
dnsm.lookup(revIp + '.cbl.abuseat.org', function(err, res) {
if(!err && res) {
dbot.say(event.server, '#dnsbl', 'ALERT: ' + ip[1] + ' connecting from ' + ip[2] + ' may well be NAUGHTY.');
}
});
}
}
this.on = 'NOTICE';
}
};
exports.fetch = function(dbot) {
return new dns(dbot);
};

View File

@ -0,0 +1,32 @@
{
"lookup-error": {
"en": "{domain} is \u000303AVAILABLE! \u000314({code})",
"cy": "{domain} \u000303AR GAEL! \u000314({code})",
"nl": "{domain} is \u000303BESCHIKBAAR! \u000314({code})",
"de": "{domain} ist \u000303VERFÜGBAR! \u000314({code})",
"fr": "{domain} est \u000303DISPONIBLE! \u000314({code})",
"it": "{domain} è \u000303DISPONIBILE! \u000314({code})"
},
"lookup": {
"en": "{domain} is \u000305TAKEN! \u000314({address})",
"cy": "Dydy {domain} \u000305DDIM AR GAEL! \u000314({address})",
"nl": "{domain} is \u000305BEZET! \u000314({address})",
"de": "{domain} ist \u000305BELEGT! \u000314({address})",
"fr": "{domain} est \u000305PRIS! \u000314({address})",
"it": "{domain} èt \u000305RISERVATO! \u000314({address})"
},
"rdns": {
"en": "{ip} \u2192 {domain}",
"fr": "{ip} \u2192 {domain}",
"it": "{ip} \u2192 {domain}",
"de":"{ip} \u2192 {domain}"
},
"rdns-error": {
"en": "Unable to lookup {ip}. \u000314({error})",
"cy": "Methu am-edrych {ip}. \u000314({error})",
"nl": "{ip} kan niet worden opgezocht. \u000314({error})",
"de": "Kann {ip} nicht auflösen. \u000314({error})",
"fr": "Impossible de rechercher {ip}. \u000314({error})",
"it": "Non è possibile cercare {ip}. \u000314({error})"
}
}

View File

@ -0,0 +1,30 @@
## Event
Emit events for whatever you want man idk.
### Description
This is a library module designed for other modules to use to emit various
events at any point, and also to attach functions to said events. These are
similar to command hooks, however their advantage is that they may be called
anywhere in your code; they are particularly useful when you want to attach a
callback to a listener.
### API
#### addHook(eventName, callback)
This function will set a given callback to be executed every time the
emit API function is executed with the given event name. The arguments of your
callback are defined as an array in the emit call.
The best place to add hooks to commands is in the 'onLoad' function of your
module, as this ensures it will be run while all other modules are loaded so
nothing will be missed.
#### emit(eventName, [ arguments ])
This function executes all of the functions associated with the given eventName,
passing your given array of arguments.
For example, to emit an event when you detect a nick change:
dbot.api.event.emit('nick_changed', [ event.server, newNick ]);

View File

@ -0,0 +1,28 @@
/**
* Module Name: event
* Description: Allow other modules to emit events and that
*/
var _ = require('underscore')._;
var event = function(dbot) {
this.dbot = dbot;
this.hooks = {};
this.api = {
'addHook': function(eventName, callback) {
if(!_.has(this.hooks, eventName)) this.hooks[eventName] = [];
this.hooks[eventName].push(callback);
},
'emit': function(eventName, args) {
if(_.has(this.hooks, eventName)) {
_.each(this.hooks[eventName], function(callback) {
callback.apply(callback.module, args);
});
}
}
};
}
exports.fetch = function(dbot) {
return new event(dbot);
};

View File

@ -0,0 +1,9 @@
## Flashy
Make big flashy text at people.
### Commands
#### ~flashy [color] [message]
Give a link to a page hosted by the flashy module which produces big flashing
text in the given colour.

View File

@ -0,0 +1,45 @@
/**
* Module Name: Flashy
* Description: Makes pages with flashing text and that innit.
*/
var _ = require('underscore')._;
var flashy = function(dbot) {
this.colourMap = {
'white': 'FFFFFF',
'red': 'FF0000',
'green': '00FF00',
'blue': '0000FF',
'yellow': 'FFFF00',
'pink': 'FFC0CB',
'magenta': 'FF00FF',
'purple': 'AA00FF',
'cyan': '00FFFF',
'orange': 'FFAA00',
'lime': 'AAFF00',
'grey': 'AAAAAA',
'infrared': '000000'
};
this.commands = {
'~flashy': function(event) {
var colour = event.input[1];
var text = event.input[2].trim().toUpperCase();
if(_.has(this.colourMap, colour)) {
event.reply(dbot.api.web.getUrl('flashy/' + colour + '/' +
encodeURIComponent(text)));
} else {
var possibleColours = _.keys(this.colourMap).join(', ') + '.';
event.reply('No such colour, brah. Available colours are: ' + possibleColours);
}
}
};
this.commands['~flashy'].regex = [/^flashy ([^ ]+) (.+)$/, 3];
};
exports.fetch = function(dbot) {
return new flashy(dbot);
};

View File

@ -0,0 +1,21 @@
var _ = require('underscore')._;
var pages = function(dbot) {
return {
'/flashy/:colour/:text': function(req, res) {
if(!_.has(this.colourMap, req.params.colour)) {
req.params.colour = 'red';
}
var colour = this.colourMap[req.params.colour];
res.render('flashy', {
'name': dbot.config.name,
'colour': colour,
'text': decodeURIComponent(req.params.text)
});
}
};
};
exports.fetch = function(dbot) {
return pages(dbot);
};

View File

@ -0,0 +1,4 @@
{
"outputPrefix": "\u000311food\u000f",
"api_key": "http://food2fork.com/about/api"
}

View File

@ -0,0 +1,60 @@
/**
* Module name: Food
* Description: recipe search
*/
var _ = require('underscore')._,
request = require('request');
var food = function(dbot) {
this.commands = {
'~recipe': function(event) {
request.get('http://food2fork.com/api/search', {
'qs': {
'key': this.config.api_key,
'q': event.input[1]
},
'json': true
}, function(error, response, body) {
if(_.isObject(body) && _.has(body, 'recipes') && body.recipes.length > 0) {
var num = _.random(0, body.recipes.length - 1),
recipe = body.recipes[num];
event.reply(dbot.t('recipe', {
'title': recipe.title,
'link': recipe.source_url
}));
} else {
event.reply(dbot.t('no_recipe'));
}
}.bind(this));
}
};
this.commands['~recipe'].regex = [/^recipe (.+)$/, 2];
this.listener = function(event) {
var match = event.message.match(new RegExp(dbot.config.name + ': what should i (have|eat|make)\\??( for (dinner|lunch|breakfast))?\\??', 'i'));
if(match) {
var page = _.random(0, 200);
request.get('http://food2fork.com/api/search', {
'qs': {
'key': this.config.api_key,
'page': page
},
'json': true
}, function(error, response, body) {
if(_.isObject(body) && _.has(body, 'recipes') && body.recipes.length > 0) {
var num = _.random(0, body.recipes.length - 1),
recipe = body.recipes[num];
event.reply(event.user + ': You should make ' + recipe.title + '. See: ' + recipe.source_url);
}
}.bind(this));
}
}.bind(this);
this.on = 'PRIVMSG';
};
exports.fetch = function(dbot) {
return new food(dbot);
};

View File

@ -0,0 +1,8 @@
{
"recipe": {
"en": "{title} - {link}"
},
"no_recipe": {
"en": "No recipes found."
}
}

View File

@ -0,0 +1,34 @@
## 500px
Adds various 500px functionality.
### Description
This module provides a command which allows users to search for a random popular 500px photo.
### Dependencies
It has following dependencies:
+ [node-500px](https://github.com/ro-ka/node-500px)
### config.json
ignorable and consumerKey has to be configurated. It can be obtained at http://developers.500px.com
```
{
"ignorable": true,
"api_key": "CONSUMERKEY_HERE"
}
```
### Commands
~r500px
Responds with a random popular 500px photo.
Example:
+ ~r500px
### TODO
Photo by user etc.

View File

@ -0,0 +1,4 @@
{
"ignorable": true,
"api_key": "CONSUMERKEY_HERE"
}

33
modules-stock/fpx/fpx.js Normal file
View File

@ -0,0 +1,33 @@
/**
* Module Name: 500px
* Description: Adds various 500px functionality.
* Requires: node-500px [http://mjgil.github.io/five-px/]
*/
var _ = require('underscore')._,
API500px = require('500px').API500px;
var fpx = function(dbot) {
this.commands = {
'~r500px': function(event) {
var random = Math.floor(Math.random() * 30);
this.api500px.photos.getPopular({'sort': 'created_at', 'rpp': '30'}, function(error, results) {
if (error) {
event.reply(dbot.t('5px_error'));
console.log(error);
} else {
var name = results.photos[random].name,
id = results.photos[random].id;
event.reply(dbot.t('5px_result',{'name':name,'id':id}));
}
});
}
};
this.onLoad = function() {
this.api500px = new API500px(this.config.api_key);
}.bind(this);
};
exports.fetch = function(dbot) {
return new fpx(dbot);
};

View File

@ -0,0 +1,8 @@
{
"5px_result":{
"en": "{name} - http://500px.com/photo/{id}"
},
"5px_error": {
"en": "Something went wrong :( Example: '~r500px'"
}
}

View File

@ -0,0 +1,3 @@
{
"~r500px": "~r500px"
}

View File

@ -0,0 +1,18 @@
Copyright (c) 2013 Douglas Gardner <douglas@chippy.ch>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,28 @@
## Github
Grabs interesting data from the GitHub API.
### Description
This module for [depressionbot](https://github.com/reality/depressionbot) takes some interesting information about Github and parses it in a pleasing manner.
### Configuration
#### defaultrepo
When repository information is lacking from the command, this repository will be used.
#### sortorder
Defines the behaviour of ~issue when no arguments are given. Options are ``created``, ``updated``, or ``comments``.
### Commands
#### ~commits
Returns the number of commits in the repository of the current depressionbot instance.
#### ~gstatus
Returns the [current status of Github](https://status.github.com), and a message explaining the current state of affairs.
#### ~issue (user/repo) [id]
Gives information about the isse pecified, from the default repository if one is not explicitly stated.
#### ~milestone [milestone name]
Returns milestone progress for any given milestone, with a link to the milestone in question.
#### ~repo (repo name)
Returns information about the repo given as a parameter. The repo should be specified as ``user/name``; for example, ``twitter/snowflake``.
#### ~repocount [user]
Returns the number of public Github repositories for the specified user.
### Dependencies
* [request](https://github.com/mikeal/request/):``$ npm install request``

View File

@ -0,0 +1,8 @@
{
"dependencies": [ "command" ],
"ignorable": true,
"help": "http://github.com/zuzak/dbot-github/blob/master/README.md",
"defaultrepo": "reality/dbot",
"sortorder": "updated",
"useragent": "reality/depressionbot github module"
}

View File

@ -0,0 +1,192 @@
/**
* Module Name: Github
* Description: Retrieves interesting Github information
*/
var request = require('request'),
exec = require('child_process').exec;
var github = function(dbot) {
this.api = {
"githubStatus": function(callback){
var reqUrl = "https://status.github.com/api/last-message.json";
request({"url": reqUrl, "headers": {"User-Agent": this.config.useragent}}, function(error, response, body) {
callback(JSON.parse(body));
});
}
};
var commands = {
'~repocount': function(event) {
var reqUrl = "https://api.github.com/users/" + event.params[1] + "/repos";
request({"url": reqUrl, "headers": {"User-Agent": this.config.useragent}}, function(error, response, body) {
if(response.statusCode == "200") {
var result = JSON.parse(body);
event.reply(dbot.t("repocount",{"user": event.params[1], "count": result.length}));
} else {
event.reply(dbot.t("usernotfound"));
}
});
},
'~repo': function(event) {
var repo = event.params[1];
if (typeof repo == 'undefined') {
repo = this.config.defaultrepo;
}
var reqUrl = "https://api.github.com/";
reqUrl += "repos/" + repo;
request({"url": reqUrl, "headers": {"User-Agent": this.config.useragent}}, function(error, response, body) {
var data = JSON.parse(body);
if (data["fork"]) {
event.reply(dbot.t("forkedrepo",data));
} else {
event.reply(dbot.t("unforkedrepo",data));
}
// TODO: move this shizz into an api call
var longurl = "http://github.com/" + repo;
event.reply(dbot.t('location')+" "+longurl);
});
},
'~gstatus': function(event) {
data = this.api.githubStatus(function(data){
event.reply(dbot.t("status"+data["status"]));
event.reply(data["body"]);
}.bind(this));
},
'~milestone': function(event) {
var repo = this.config.defaultrepo;
var name = event.params[1];
if (event.params[2]){
repo = name;
name = event.params[2];
}
var reqUrl = "https://api.github.com/repos/";
reqUrl += repo + "/milestones";
request({"url": reqUrl, "headers":{"User-Agent": this.config.useragent}}, function(error, response, body) {
var data = JSON.parse(body);
for (var section in data) {
var milestone = data[section];
if (milestone["title"] == name){
var str = "Milestone " + milestone["title"];
var progress = milestone["closed_issues"] / (milestone["open_issues"] + milestone["closed_issues"]);
progress = Math.round(progress*100);
var bar = "[";
for (var i = 10; i < 100; i += 10) {
if ((progress/i) > 1) {
bar += "█";
} else {
bar += " ";
}
}
bar += "]";
str += " is " + bar + progress + "% complete";
event.reply(str);
break;
}
}
});
},
'~repocount': function(event) {
// TODO: add handling for non existent user
var reqUrl = "https://api.github.com/users/" + event.params[1] + "/repos";
request({"url": reqUrl,"headers": { "User-Agent": this.config.useragent}}, function(error, response, body) {
var result = JSON.parse(body);
event.reply(event.params[1] + " has " + result.length + " public repositories.");
});
},
'~grate': function(event) {
request.get({"url":"https://api.github.com/rate_limit", "headers":{"User-Agent": this.config.useragent}}, function(error, response, body) {
var data = JSON.parse(body);
if (data.message){
event.reply(data.message);
} else {
event.reply(data.rate.remaining + " requests of " + data.rate.limit + " remaining.");
}
});
},
'~issue': function(event) {
var repo, issue, randflag;
if (isNaN(event.params[1]) && event.params[1]){ // if ~issue foo/bar
repo = event.params[1];
issue = event.params[2];
} else {
repo = this.config.defaultrepo;
issue = event.params[1];
}
if (issue == "*" || issue == "random" || issue == "0") {
issue = "";
randflag = true;
} else if (!issue) { // issue is undefined
issue = "";
} else {
issue = "/" + issue; // got to be a better way
}
var reqUrl = "https://api.github.com/repos/" + repo + "/issues" +
issue + "?sort=" + this.config.sortorder;
request.get({"url": reqUrl, headers: { "User-Agent": this.config.useragent}}, function(error,response, body) {
if (response.statusCode == "200") {
var data = JSON.parse(body);
if (!issue){
if (randflag) {
data = data[Math.floor(Math.random() * data.length)];
} else {
data = data[0];
}
}
if (_.has(data["pull_request"], "html_url")){
data["pull_request"] = " with code";
} else {
data["pull_request"] = "";
}
if (data["state"]=="open") {
data["state"] = "\u000303" + data["state"];
} else {
data["state"] = "\u000304" + data["state"];
}
var labels = "";
for (var i=0; i < data["labels"].length; i++) { // for-in doesn't like me
var color = "\u0003" + (parseInt(data["labels"][i]["color"],16) % 15);
labels += " " + color + data["labels"][i]["name"];
}
data["label"] = labels;
event.reply(dbot.t("issue",data));
event.reply(data["html_url"]);
} else {
event.reply(dbot.t("issuenotfound"));
}
});
},
'~commits': function(event) {
exec("git rev-list --all | wc -l", function(error, stdout, stderr) {
stdout = stdout.trim();
request({"url":"http://numbersapi.com/" + stdout + "?fragment&default=XXX"}, function(error, response, body){
if (body != "XXX"){
event.reply(dbot.t("commitcountfun",{"fact": body, "count": stdout}));
} else {
// nothing fun about the number, let's try the year
request({"url":"http://numbersapi.com/" + stdout + "/year?fragment&default=XXX"}, function(error, response, body){
if (body != "XXX"){
event.reply(dbot.t("commitcountyear",{"fact": body, "count": stdout}));
} else {
event.reply(dbot.t("commitcountboring",{"count": stdout}));
}
});
}
});
});
}
};
this.commands = commands;
this.on = 'PRIVMSG';
};
exports.fetch = function(dbot) {
return new github(dbot);
};

View File

@ -0,0 +1,94 @@
{
"repocount": {
"en": "{user} has {count} public repos.",
"cy": "Mae {count} archifdai cyhoeddus gan {user}.",
"de": "{user} hat {count} öffnetliche Repos.",
"fr": "{user} a {count} dépôt(s) public(s).",
"it": "{user} ha {count} deposito/i pubblico/i."
},
"statusgood": {
"en": "\u000309Shit's fine",
"cy": "\u000309Cachu'n ddirwy",
"de": "\u000309Alles in Ordnung",
"fr": "\u000309Cette merde tourne bien",
"it": "\u000309Funziona a meraviglia."
},
"statusminor": {
"en": "\u000308Shit's touchy",
"cy": "\u000308Cachu'n fregus",
"de": "\u000308Kleinere Probleme vorhanden",
"fr": "\u000308Cette merde a un petit problème",
"it": "\u000308Piccoli problemi all' orizzonte"
},
"statusmajor": {
"en": "\u000304Shit's fucked:",
"cy": "\u000304Cachu wedi cyrraedd y ffan:",
"de": "\u000304Du bist am Arsch",
"fr": "\u000304Cette merde est foutue : ",
"it": "\u000304Sei nella merda : "
},
"location": {
"en": "You can find that shit at:",
"cy": "Gallwch ddod o hyd y cachu yn:",
"de": "Kann gefunden werden unter:",
"fr": "Tu peux trouver cette merde ici : ",
"it": "Puoi trovare questa coglionata a: "
},
"forkedrepo": {
"en": "{name} is a forked {language} repo with {open_issues} unresolved issues [{forks}F {watchers}W]",
"cy": "{name} ydy archif {language} fforchog gyda {open_issues} materion heb eu datrys [{forks}F {watchers}W]",
"de": "{name} ist eine geteilte {language} Repo mit {open_issues} ungelösten Problemen [{forks}F {watchers}W]",
"fr": "{name} est un dépôt fourché {language} avec {open_issues} problème(s) non résolu(s) [{forks}F {watchers}W]",
"it": "{name} è un deposito biforcato {language} con {open_issues} problema/i irrisolto/i [{forks}F {watchers}W]"
},
"unforkedrepo": {
"en": "{name} is a {language} repo with {open_issues} unresolved issues [{forks}F {watchers}W]",
"cy": "{name} ydy archif {language} gyda {open_issues} materion heb eu datrys [{forks}F {watchers}W]",
"de": "{name} ist eine {language} Repo mit {open_issues} ungelösten Problemen [{forks}F {watchers}W]",
"fr": "{name} est un dépôt {language} avec {open_issues} problème(s) non résolu(s) [{forks}F {watchers}W]",
"it": "{name} è un deposito {language} con {open_issues} problema/i irrisolto/i [{forks}F {watchers}W]"
},
"usernotfound": {
"en": "User not found.",
"cy": "Defnyddiwr heb ei ganfod.",
"de": "Benutzer nicht gefunden.",
"fr": "Utilisateur non trouvé.",
"it": "Utente non trovato."
},
"issuenotfound": {
"en": "Unable to find that issue.",
"cy": "Wedi methu dod o hyd mater hwnnw",
"de": "Kann dieses Problem nicht finden.",
"fr": "Impossible de trouver ce problème.",
"it": "Impossibile trovare questo problema."
},
"issue": {
"en": "Issue \u000308{number}\u0003: {title} [{state}{pull_request}\u000315; {comments} comments]{label}",
"cy": "Mater \u000308{number}\u0003: {title} [{state}{pull_request}\u000315; {comments} sylwadau]{label}",
"de": "Problem \u000308{number}\u0003: {title} [{state}{pull_request}\u000315; {comments} comments]{label}",
"fr": "Problème \u000308{number}\u0003: {title} [{state}{pull_request}\u000315; {comments} commentaires]{label}",
"it": "Problema \u000308{number}\u0003: {title} [{state}{pull_request}\u000315; {comments} commento/i]{label}"
},
"commitcountboring": {
"en": "My code has been committed {count} times.",
"cy": "Mae fy cod wedi cael ei gyflawni ar {count} adegau.",
"de": "Mein Code wurde {count} mal bestätigt.",
"fr": "Mon code a été modifié {count} fois.",
"it": "Il mio codice è stato modificato {count} volta/e."
},
"commitcountfun": {
"en": "My repository has the same number of commits as {fact} ({count}).",
"cy": "Yr un nifer o ymrwymo fel {fact} gan fy archif ({count}).",
"de": "Meine Repository hat die gleiche Anzahl Commits wie {fact} ({count}).",
"fr": "Mon dépôt a le même nombre de modifications que {fact} ({count}).",
"it": "Il mio deposito ha lo stesso numero di modifiche come {fact} ({count})."
},
"commitcountyear": {
"en": "My repository's commits number {count}, the year that {fact}.",
"cy": "Nifer o ymrwymo gan fy archif: {count}, y flwyddyn y {fact}.",
"de": "Anzahl der Commits in meinem Repository {count}, des Jahres {fact}",
"fr": "Mon dépot compte {count} modifications, l'année où {fact}.",
"it": "Il mio deposito ha {count} modifica/che, l' anno che {fact}."
}
}

View File

@ -0,0 +1,29 @@
/**
* Module Name: Google Maps
* Description: GMaps and ting
*/
var gm = require('googlemaps'),
_ = require('underscore')._;
var gmaps = function(dbot) {
this.commands = {
'~from': function(event) {
var from = event.input[1],
to = event.input[2],
departureNow = Math.floor((new Date()).getTime()/1000);
gm.directions(from, to, function(err, result) {
if(!err && result && result.status !== 'ZERO_RESULTS') {
event.reply('If you leave right now, it will take ' + result.routes[0].legs[0].duration.text + ' to get from ' + from + ' to ' + to + ' via public transport.');
} else {
event.reply('Apparently one cannot get from ' + from + ' to ' + to + ' using public transport. Do you accept the challenge?');
}
}, 'false', 'transit', null, null,null, null, null, departureNow);
}
};
this.commands['~from'].regex = [/^from (.*) to (.*)/, 3];
};
exports.fetch = function(dbot) {
return new gmaps(dbot);
};

View File

@ -0,0 +1,4 @@
{
"api_key": "sethere",
"outputPrefix": "\u00033goodreads\u000f"
}

View File

@ -0,0 +1,293 @@
/**
* Module Name: GoodReads
* Description: Interacts with the GoodReads API to provide book-oriented functionality to dbot
*/
var util = require('util'),
_ = require('underscore')._,
rp = require('request-promise-native'),
parseString = util.promisify(require('xml2js').parseString);
var GoodReads = function(dbot) {
this.apiRoot = 'https://www.goodreads.com';
this.internalAPI = {
'outputError': (evt, e) => {
switch(e) {
case 'goodreads-error': evt.reply('Error talking to GoodReads.'); return;
case 'book-not-found': evt.reply(dbot.t('gr_nobook')); return;
case 'no-description': evt.reply('No description was found for the book you asked for.'); return;
case 'author-not-found': evt.reply(dbot.t('gr_noauthor')); return;
}
console.log(e);
evt.reply('Something went wrong and I don\'t know what.');
},
'formatProfile': profile => {
var shelves = {};
_.each(profile.user_shelves.user_shelf, shelf => {
shelves[shelf.name] = shelf.book_count['_'];
});
profile.user_shelves = shelves;
return profile;
}
};
this.api = {
'findBook': async term => {
//https://www.goodreads.com/search/index.xml
var body = await rp({
uri: this.apiRoot + '/search/index.xml',
qs: {
key: this.config.api_key,
q: term.split(' ').join('+')
}
});
var response = await parseString(body, { explicitArray: false });
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
var result = response.GoodreadsResponse.search.results;
if(!result || !_.has(result, 'work')) throw 'book-not-found';
if(!result.work[0]) throw 'book-not-found';
return {
id: result.work[0].best_book.id['_'],
title: result.work[0].best_book.title,
author: result.work[0].best_book.author.name,
rating: result.work[0].average_rating
};
},
'getSummaryForBook': async id => {
//https://www.goodreads.com/book/show.xml
var body = await rp({
uri: this.apiRoot + '/book/show.xml',
qs: {
key: this.config.api_key,
id: id
}
});
var response = await parseString(body, { explicitArray: false });
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
var result = response.GoodreadsResponse.book;
if(!result) throw 'book-not-found';
if(!_.has(result, 'description')) throw 'no-description';
return result.description;
},
'findAuthor': async term => {
//https://www.goodreads.com/api/author_url/<ID>
var body = await rp({
url: this.apiRoot + '/api/author_url/' + term,
qs: {
key: this.config.api_key
}
});
var response = await parseString(body, {explicitArray: false });
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
var result = response.GoodreadsResponse.author;
if(!result) throw 'author-not-found';
return {
id: result['$'].id,
author: result.name
};
},
'getProfileById': async id => {
//https://www.goodreads.com/user/show.xml
try {
var body = await rp({
url: this.apiRoot + '/user/show.xml',
qs: {
key: this.config.api_key,
id: id
}
});
}
catch (e) {
if(e.statusCode && e.statusCode == 404) {
throw 'user-not-found';
return;
}
throw e;
}
var response = await parseString(body, { explicitArray: false });
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
var result = response.GoodreadsResponse.user;
if(!result) throw 'user-not-found';
return this.internalAPI.formatProfile(result);
},
'getProfileByName': async username => {
//https://www.goodreads.com/user/show.xml
try {
var body = await rp({
url: this.apiRoot + '/user/show.xml',
qs: {
key: this.config.api_key,
username: username
}
});
}
catch (e) {
if(e.statusCode && e.statusCode == 404) {
throw 'user-not-found';
return;
}
throw e;
}
var response = await parseString(body, { explicitArray: false });
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
var result = response.GoodreadsResponse.user;
if(!result) throw 'user-not-found';
return this.internalAPI.formatProfile(result);
},
'getShelfForUserId': async (id, shelf) => {
//https://www.goodreads.com/review/list.xml?v=2
var body = await rp({
url: this.apiRoot + '/review/list.xml',
qs: {
v: '2',
key: this.config.api_key,
id: id,
shelf: shelf
}
});
var response = await parseString(body, { explicitArray: false });
if(!_.has(response, 'GoodreadsResponse')) throw 'goodreads-error';
let result = response.GoodreadsResponse.reviews.review;
if(!result) return [];
if(!_.isArray(result)) {
result = [result];
}
return _.map(result, r => {
return {
id: r.book.id['_'],
title: r.book.title_without_series
};
});
}
};
this.commands = {
'~book' : async evt => {
try {
var book = await this.api.findBook(evt.input[1]);
evt.reply(dbot.t('gr_book', {
author: book.author,
title: book.title,
rating: book.rating,
link: this.apiRoot + '/book/show/' + book.id
}));
}
catch(e) { this.internalAPI.outputError(evt, e); }
},
'~booksummary': async evt => {
try {
var book = await this.api.findBook(evt.input[1]);
var summary = await this.api.getSummaryForBook(book.id);
evt.reply(dbot.t('gr_summary', {
title: book.title,
summary: summary,
link: this.apiRoot + '/book/show/' + book.id
}));
}
catch(e) { this.internalAPI.outputError(evt, e); }
},
'~author' : async evt => {
try {
evt.reply(dbot.t('gr_author', await this.api.findAuthor(evt.input[1])));
}
catch(e) { this.internalAPI.outputError(evt, e); }
},
'~reading': async (evt, profile) => {
try {
let books = await this.api.getShelfForUserId(profile.id, 'currently-reading');
var booksCount = books.length;
if(!booksCount) {
evt.reply(dbot.t('gr_not_reading', { user: evt.rUser.currentNick }));
return;
}
let tooMany = booksCount > 5;
if (tooMany) books = _.sample(books, 5);
evt.reply(dbot.t('gr_is_reading', { user: evt.rUser.currentNick, count: booksCount }));
_.each(books, b => {
evt.reply(ostr = b.title + ' - https://www.goodreads.com/book/show/' + b.id);
});
if (tooMany) {
evt.reply('... And ' + (booksCount - 5) + ' more - https://www.goodreads.com/review/list/' + profile.id + '?shelf=currently-reading');
}
}
catch(e) { this.internalAPI.outputError(evt, e); }
}
};
this.commands['~book'].regex = [/^book (.*)/, 2];
this.commands['~booksummary'].regex = [/^booksummary (.*)/, 2];
this.commands['~author'].regex = [/^author ([\d\w\s-]*)/, 2];
this.commands['~reading'].requiresProfile = true;
_.each(this.commands, ((cmd, cmdName) => {
if(cmd.requiresProfile) {
this.commands[cmdName] = (async evt => {
var grUsername = evt.rProfile.goodreads;
if(!grUsername) {
evt.reply(evt.rUser.currentNick + ': Set a Goodreads username with "~set goodreads username"');
return;
}
let grId = evt.rProfile.goodreads_id;
try {
var profile;
if(grId) {
profile = await this.api.getProfileById(grId);
} else {
profile = await this.api.getProfileByName(grUsername);
grId = profile.id;
dbot.api.profile.setProperty(evt.server, evt.user, 'goodreads_id', grId, function(){});
}
await cmd(evt, profile);
}
catch(e) {
if(e === 'user-not-found') evt.reply('User not found. Is your GoodReads username set correctly?');
else this.internalAPI.outputError(evt, e);
}
}).bind(this);
}
}).bind(this))
}
exports.fetch = dbot => new GoodReads(dbot);

View File

@ -0,0 +1,23 @@
{
"gr_book": {
"en": "[{title} by {author} - {rating}] - {link}"
},
"gr_summary": {
"en": "[{title}] - {summary} - {link}"
},
"gr_author": {
"en": "[{author}] - https://www.goodreads.com/author/show/{id}"
},
"gr_nobook": {
"en": "No book by that name was found."
},
"gr_noauthor": {
"en": "No author by that name was found."
},
"gr_not_reading": {
"en": "{user} is not currently reading any books."
},
"gr_is_reading": {
"en": "{user} is currently reading the following {count} books:"
}
}

View File

@ -0,0 +1,6 @@
{
"~book": "~book [bookname] - returns the title, author, rating, and GoodReads link",
"~booksummary": "~booksummary [bookname] - returns the summary for the requested book",
"~author": "~author [authorname] - returns the GoodReads link for the requested author",
"~reading": "~reading - displays up to 5 of the books you are currently reading"
}

View File

@ -0,0 +1,26 @@
## Ignore
Ignore modules.
### Description
Commands with which users can choose to ignore listeners and commands from
certain modules persistently, by storing their choices in the database. This is
an interface for the JSBot ignoreTag functionality which actually implements
the ignoration.
### Configuration
All modules may return with them an 'ignorable' property, which defines whether
or not their functionality may be ignored by users.
### Commands
#### ~ignore [module]
Ignore a given module. If the user does not specify a module, or provides an
invalid one a list of modules which are available to ignore will be given.
#### ~unignore [module]
Unignore a previously ignored module. If the user does not specify a module, or
provides an invalid choice a list of modules which are currently ignored will be
given.

View File

@ -0,0 +1,49 @@
var _ = require('underscore')._;
var api = function(dbot) {
return {
// Is user ignoring command/module?
'isUserIgnoring': function(user, item, callback) {
this.internalAPI.isUserImpeded(user, item, 'ignores', callback);
},
// Is user banned from command/module?
'isUserBanned': function(user, item, callback) {
this.internalAPI.isUserImpeded(user, item, 'bans', callback);
},
// Is channel ignoring module?
// TODO: Command support
'isChannelIgnoring': function(channelName, item, callback) {
var isIgnoring = false,
channel = false;
this.db.search('channel_ignores', {
'server': server,
'name': channel
}, function(result) {
channel = result;
}, function(err) {
if(!err && channel && _.include(channel.ignores, item)) {
isIgnoring = true;
}
callback(isIgnoring);
});
},
// Resolve a nick and return their user and ignores object
'getUserIgnores': function(user, callback) {
this.db.read('ignores', user.id, function(err, ignores) {
if(!err && ignores) {
callback(false, ignores);
} else {
callback(true, null);
}
});
}
};
}
exports.fetch = function(dbot) {
return api(dbot);
};

View File

@ -0,0 +1,6 @@
{
"ignorable": false,
"dependencies": [ "users" ],
"dbKeys": [ "ignores", "bans" ],
"dbType": "redis"
}

View File

@ -0,0 +1,304 @@
/**
* Module Name: Ignore
* Description: Handles commands in which users can choose to ignore listeners
* and commands from certain modules. It also populates the JSBot instance with
* this information, since that actually performs the ignorance. Also provides
* commands for moderators to choose the bot to ignore certain channels.
*/
var _ = require('underscore')._,
databank = require('databank'),
uuid = require('node-uuid'),
NoSuchThingError = databank.NoSuchThingError;
var ignore = function(dbot) {
this.internalAPI = {
'isUserImpeded': function(user, item, by, callback) {
this.api.getUserIgnores(user, function(err, ignores) {
var isImpeded = false;
if(!err && ignores) {
if(_.has(dbot.commands, item) && !_.include(ignores[by], item)) {
item = dbot.commands[item].module;
}
if(_.include(ignores[by], item) || _.include(ignores[by], '*')) {
isImpeded = true;
}
}
callback(isImpeded);
});
}.bind(this)
};
var commands = {
'~ignore': function(event) {
var module = event.params[1];
var ignorableModules = _.chain(dbot.modules)
.filter(function(module, name) {
return dbot.config.modules[module].ignorable === true;
})
.pluck('name')
.value();
if(_.isUndefined(module)) {
event.reply(dbot.t('ignore_usage', {
'user': event.user,
'modules': ignorableModules.join(', ')
}));
} else {
if(module == '*' || _.include(ignorableModules, module)) {
this.api.getUserIgnores(event.rUser, function(err, ignores) {
if(!ignores) {
ignores = {
'id': event.rUser.id,
'ignores': [],
'bans': []
};
}
if(!_.include(ignores.ignores, module)) {
ignores.ignores.push(module);
this.db.save('ignores', event.rUser.id, ignores, function(err) {
if(!err) {
dbot.instance.ignoreTag(event.user, module);
event.reply(dbot.t('ignored', {
'user': event.user,
'module': module
}));
}
});
} else {
event.reply(dbot.t('already_ignoring', { 'user': event.user }));
}
}.bind(this));
} else {
event.reply(dbot.t('invalid_ignore', { 'user': event.user }));
}
}
},
'~unignore': function(event) {
var module = event.params[1];
this.api.getUserIgnores(event.rUser, function(err, ignores) {
if(err || !ignores || _.isUndefined(module)) {
if(ignores) {
event.reply(dbot.t('unignore_usage', {
'user': event.user,
'modules': ignores.ignores.join(', ')
}));
} else {
event.reply(dbot.t('empty_unignore_usage', {
'user': event.user
}));
}
} else {
if(_.include(ignores.ignores, module)) {
ignores.ignores = _.without(ignores.ignores, module);
this.db.save('ignores', event.rUser.id, ignores, function(err) {
if(!err) {
dbot.instance.removeIgnore(event.user, module)
event.reply(dbot.t('unignored', {
'user': event.user,
'module': module
}));
}
});
} else {
event.reply(dbot.t('invalid_unignore', { 'user': event.user }));
}
}
}.bind(this));
},
'~ban': function(event) {
var nick = event.input[1],
item = event.input[2];
if(item == '*' || _.include(dbot.config.moduleNames, item) || _.include(dbot.commands, item)) {
dbot.api.users.resolveUser(event.server, nick, function(err, user) {
this.api.getUserIgnores(user, function(err, ignores) {
if(!ignores) {
ignores = {
'id': user.id,
'ignores': [],
'bans': []
};
}
if(!_.include(ignores.bans, item)) {
ignores.bans.push(item);
this.db.save('ignores', user.id, ignores, function(err) {
if(!err) {
event.reply(dbot.t('banned_success', {
'user': event.user,
'banned': nick,
'module': item
}));
}
});
} else {
event.reply(dbot.t('already_banned', {
'user': event.user,
'banned': nick
}));
}
}.bind(this));
}.bind(this));
} else {
event.reply(dbot.t('invalid_ban', { 'user': event.user }));
}
},
'~unban': function(event) {
var nick = event.input[1],
item = event.input[2];
dbot.api.users.resolveUser(event.server, nick, function(err, user) {
this.api.getUserIgnores(user, function(err, ignores) {
if(err || !ignores) {
event.reply(dbot.t('invalid_unban', {
'user': event.user,
'banned': nick
}));
} else {
if(_.include(ignores.bans, item)) {
ignores.bans = _.without(ignores.bans, item);
this.db.save('ignores', user.id, ignores, function(err) {
event.reply(dbot.t('unbanned_success', {
'user': event.user,
'banned': nick,
'module': item
}));
});
} else {
event.reply(dbot.t('invalid_unban', {
'user': event.user,
'banned': nick
}));
}
}
}.bind(this));
}.bind(this));
},
'~ignorechannel': function(event) {
var channelName = event.input[1],
module = event.input[2];
// Ignoring the value of 'ignorable' at the moment
if(module == '*' || _.include(dbot.config.moduleNames, module)) {
var channel = false;
this.db.search('channel_ignores', {
'server': event.server,
'name': channelName
}, function(result) {
channel = result;
}, function(err) {
if(!channel) {
var id = uuid.v4();
channel = {
'id': id,
'server': event.server,
'name': channelName,
'ignores': []
};
}
if(!_.include(channel.ignores, module)) {
channel.ignores.push(module);
this.db.save('channel_ignores', channel.id, channel, function(err) {
dbot.instance.ignoreTag(channel.name, module);
event.reply(dbot.t('ignoring_channel', {
'module': module,
'channel': channelName
}));
});
} else {
event.reply(dbot.t('already_ignoring_channel', {
'module': module,
'channel': channelName
}));
}
}.bind(this));
} else {
event.reply(dbot.t('module_not_exist', { 'module': module }));
}
},
'~unignorechannel': function(event) {
var channelName = event.input[1],
module = event.input[2],
channel = false;
this.db.search('channel_ignores', {
'server': event.server,
'name': channelName
}, function(result) {
channel = result;
}, function(err) {
if(channel && _.include(channel.ignores, module)) {
channel.ignores = _.without(channel.ignores, module);
this.db.save('channel_ignores', channel.id, channel, function(err) {
dbot.instance.removeIgnore(channel.name, module);
event.reply(dbot.t('unignoring_channel', {
'module': module,
'channel': channelName
}));
});
} else {
event.reply(dbot.t('not_ignoring_channel', {
'module': module,
'channel': channelName
}));
}
}.bind(this));
}
};
commands['~ban'].regex = [/^ban ([^ ]+) ([^ ]+)$/, 3];
commands['~unban'].regex = [/^unban ([^ ]+) ([^ ]+)$/, 3];
commands['~ignorechannel'].regex = [/^ignorechannel ([^ ]+) ([^ ]+)$/, 3];
commands['~unignorechannel'].regex = [/^unignorechannel ([^ ]+) ([^ ]+)$/, 3];
commands['~ban'].access = 'power_user';
commands['~unban'].access = 'power_user';
commands['~ignorechannel'].access = 'moderator';
commands['~unignorechannel'].access = 'moderator';
this.commands = commands;
this.onLoad = function() {
dbot.instance.clearIgnores();
this.db.scan('ignores', function(ignores) {
dbot.api.users.getUser(ignores.id, function(err, user) {
if(user) {
_.each(ignores.ignores, function(module) {
dbot.instance.ignoreTag(user.currentNick, module);
});
}
});
}, function(err) { });
this.db.scan('channel_ignores', function(channel) {
_.each(channel.ignores, function(module) {
dbot.instance.ignoreTag(channel.name, module);
});
}, function(err) { });
dbot.api.event.addHook('new_current_nick', function(user, oldNick) {
this.api.getUserIgnores(user, function(err, ignores) {
if(ignores) {
_.each(ignores.ignores, function(module) {
dbot.instance.removeIgnore(oldNick, module);
dbot.instance.ignoreTag(user.currentNick, module);
});
}
}.bind(this));
}.bind(this));
}.bind(this);
};
exports.fetch = function(dbot) {
return new ignore(dbot);
};

View File

@ -0,0 +1,183 @@
{
"ignore_usage": {
"en": "{user}: Usage: ~ignore [module]. Modules you can ignore are: {modules}.",
"es": "{user}: Modo de empleo: ~ignore [módulo]. Módulos que tú puedes ignorar son: {modules}.",
"na'vi": "{user}: Sar: ~ignore ['u]. U, nga ke tìng mikyun: {modules}.",
"cy": "{user}: Defnydd: ~ignore [modiwl]. Modiwlau a allech anwybyddu yw: {modules}.",
"nl": "{user}: Gebruik: ~ignore [module]. Modules die negeert kunnen worden zijn: {modules}.",
"de": "{user}: Benutzung: ~ignore [module]. Module, die ausgeschaltet werden können: {modules}.",
"fr": "{user}: Utilisation: ~ignore [module]. Les modules que vous pouvez ignorer sont: {modules}.",
"it": "{user}: Uso: ~ignore [module]. I moduli che puoi ignorare sono: {modules}."
},
"already_ignoring": {
"en": "{user}: You're already ignoring that module.",
"es": "{user}: Ya ignoras este módulo.",
"na'vi": "{user}: 'uri nga ke tìng mikyun srekrr.",
"cy": "{user}: Mi rwyt ti'n anwybyddu'r modiwl yna'n barod.",
"nl": "{user}: Je negeert deze module al.",
"de": "{user}: Dieses Modul ist bereits ausgeschaltet.",
"fr": "{user}: Vous ignorez déjà ce module.",
"it": "{user}: Stai già ignorando questo modulo"
},
"ignored": {
"en": "{user}: Now ignoring {module}.",
"es": "{user}: Estás ignorando {module}.",
"na'vi": "{user}: Nga ke terìng mikyun {module}ne set.",
"cy": "{user}: Nawr yn anwybyddu {module}",
"nl": "{user}: {module} wordt nu genegeerd.",
"de": "{user}: {module} wird nun ausgeschaltet.",
"fr": "{user}: {module} désormais ignoré.",
"it": "{user}: {module} sarà adesso ignorato"
},
"invalid_ignore": {
"en": "{user}: That isn't a valid module name.",
"es": "{user}: Ese no es un nombre de un módulo valido.",
"na'vi": "{user}: Tsatstxo eyawr ke lu.",
"cy": "{user}: Nid oedd hwna'n modiwl dilys",
"nl": "{user}: Dat is geen geldige modulenaam.",
"de": "{user}: Dies ist kein Name eines Moduls.",
"fr": "{user}: Ceci ne correspond pas à un nom de module valide.",
"it": "{user}: Questo non è un nome di modulo valido"
},
"unignore_usage": {
"en": "{user}: Usage: ~unignore [module]. Modules you are currently ignoring: {modules}.",
"es": "{user}: Modo de empleo: ~unignore [módulo]. Módulos que ignoras ahora mismo: {modules}.",
"na'vi": "{user}: Sar: ~unignore ['u]. Uri, nga ke terìng mikyun: {modules}.",
"cy": "{user}: Defnydd: ~unignore [modiwl]. Modiwlau rydech yn anwybyddu ar hyn o bryd: {modules}",
"nl": "{user}: Gebruik: ~unignore [module]. Modules die momenteel worden genegeerd: {modules}.",
"de": "{user}: Benutzung: ~unignore [module]. Module, die im Moment ausgeschaltet sind: {modules}.",
"fr": "{user}: Utilisation: ~unignore [module]. Modules que vous ignorez actuellement: {modules}.",
"it": "{user}: Uso: ~unignore [module]. Module che ignori attualmente: {modules}."
},
"empty_unignore_usage": {
"en": "{user}: Usage: ~unignore [module].",
"es": "{user}: Modo de empleo: ~unignore [módulo].",
"na'vi": "{user}: Sar: ~unignore ['u].",
"cy": "{user}: Defnydd: ~unignore [modiwl].",
"nl": "{user}: Gebruik: ~unignore [module].",
"de": "{user}: Benutzung: ~unignore [module].",
"fr": "{user}: Utilisation: ~unignore [module].",
"it": "{user}: Uso: ~unignore [module]."
},
"invalid_unignore": {
"en": "{user}: You're not ignoring that module or it doesn't exist.",
"es": "{user}: No ignoras este módulo o no existe.",
"na'vi":"{user}: Nga terìng mikyun fu fì'ul fìtsengit ke tok.",
"cy": "{user}: Nid wyt ti'n anwybyddu'r modiwl yna neu nid yw e'n bodoli",
"nl": "{user}: Deze module bestaat niet of wordt niet genegeerd.",
"de": "{user}: Dieses Modul ist entweder ausgeschaltet oder existiert nicht.",
"fr": "{user}: Vous n'ignorez pas ce module ou il n'existe pas.",
"it": "{user}: Non stai ignorando questo modulo o non esiste."
},
"unignored": {
"en": "{user}: No longer ignoring {module}.",
"es": "{user}: Ya no ignoras {module}.",
"na'vi": "{user}: Nga terìng mikyun {module}ne set",
"cy": "{user}: Ddim yn anwybyddu {module} bellach",
"nl": "{user}: {module} wordt niet langer genegeerd.",
"de": "{user}: {module} ist nicht länger ausgeschaltet.",
"fr": "{user}: {module} n'est plus ignoré à présent.",
"it": "{user}: {module} non viene attualmente più ignorato"
},
"ban_usage": {
"en": "{user}: Usage: ~ban [user] [module/command]. Use * for all modules and commands.",
"cy": "{user}: Defnydd: ~ban [defnyddiwr] [modiwl/gorchymyn]. Defnyddio * am pob modiwlau a gorchmynion.",
"nl": "{user}: Gebruik: ~ban [gebruiker] [module/commando]. Gebruik * voor alle modules en alle commandos.",
"de": "{user}: Benutzung ~ban [Benutzer] [module/Befehl]. Benutze * für alle Module und Befehle.",
"fr": "{user}: Utilisation: ~ban [user] [module/command]. Utilisez * pour tous les modules et commandes.",
"it": "{user}: Uso: ~ban [user] [module/command]. Utilizza * per tutti i moduli e commandi."
},
"already_banned": {
"en": "{user}: {banned} is already banned from that module.",
"cy": "{user}: {banned} eisoes wedi ei wahardd o'r modiwl.",
"nl": "{user}: {banned} is al geband van deze module.",
"de": "{user}: {banned} ist bereits von diesem Modul gebannt.",
"fr": "{user}: {banned} est déjà interdit d'utiliser ce module.",
"it": "{user}: {banned} è già bandito da usare questo modulo."
},
"banned_success": {
"en": "{user}: {banned} is now banned from {module}.",
"cy": "{user}: {banned} ei wahardd yn awr am {module}.",
"nl": "{user}: {banned} mag {module} nu niet meer gebruiken.",
"de": "{user}: {banned} ist nun von {module} gebannt.",
"fr": "{user}: {banned} est maintenant interdit d'utiliser {module}.",
"it": "{user}: {banned} è stato adesso bandito da usare {module}."
},
"invalid_ban": {
"en": "{user}: That isn't a valid module name.",
"cy": "{user}: Nid oedd hwna'n modiwl dilys",
"nl": "{user}: Dat is geen geldige modulenaam.",
"de": "{user}: Dies ist kein Name eines Moduls.",
"fr": "{user}: Ceci n'est pas un nom de module valide.",
"it": "{user}: Queso non è il nome di un modulo valido."
},
"unban_usage": {
"en": "{user}: Usage: ~unban [user] [module].",
"cy": "{user}: Defnydd: ~unban [defnyddiwr] [modiwl].",
"nl": "{user}: Gebruik: ~unban [gebruiker] [module].",
"de": "{user}: Benutzung: ~unban [Benutzer] [module].",
"fr": "{user}: Utilisation: ~unban [user] [module].",
"it": "{user}: Uso: ~unban [user] [module]."
},
"invalid_unban": {
"en": "{user}: {banned} is not banned from that module or it doesn't exist.",
"cy": "{user}: Nid oedd {banned} wedi ei wahardd o'r modiwl, neu nid yw'n bodoli.",
"nl": "{user}: {banned} is niet geband van die module of de module bestaat niet.",
"de": "{user}: {banned} ist von diesem Modul nicht gebannt, oder es existiert nicht.",
"fr": "{user}: {banned} n'est pas interdit d'utiliser ce module, ou il n'existe pas.",
"it": "{user}: {banned} non è stato bandito da questo modulo o non esiste."
},
"unbanned_success": {
"en": "{user}: {banned} is no longer banned from {module}.",
"cy": "{user}: Nid yw {banned} yn cael ei wahardd mwyach.",
"nl": "{user}: {banned} mag {module} weer gebruiken.",
"de": "{user}: {banned} wurde von {module} entbannt.",
"fr": "{user}: {banned} n'est plus interdit d'utiliser {module}.",
"it": "{user}: {banned} non è più bandito dall' utilizzare {module}."
},
"ignoring_channel": {
"en": "Now ignoring {module} in {channel}.",
"na'vi": "Oe ke stayawm {module}ur mì {channel}",
"cy": "Bellach yn anwybyddu {module} yn {channel}.",
"nl": "{module} wordt nu genegeerd in {channel}.",
"de": "{module} in {channel} ist nun ausgeschaltet.",
"fr": "{module} dans {channel} maintenant ignoré.",
"it": "{module} in {channel} viene adesso ignorato."
},
"already_ignoring_channel": {
"en": "Already ignoring {module} in {channel}.",
"na'vi": "Oe ke stayawm {module}ur mì {channel} li",
"cy": "Eisoes anwybyddu {module} yn {channel}",
"nl": "{module} wordt al genegeerd in {channel}.",
"de": "{module} in {channel} ist bereits ausgeschaltet.",
"fr": "{module} dans {channel} déjà ignoré.",
"it": "{module} in {channel} già ignorato."
},
"module_not_exist": {
"en": "{module} isn't loaded or doesn't exist.",
"na'vi": "Oel ke omum teri {module}it fu {module} ke fkeytok",
"cy": "Dydy {module} ddim yn lwythodd, neu ddim yn bodoli.",
"nl": "{module} is niet geladen of bestaat niet.",
"de": "{module} ist nicht geladen oder existiert nicht.",
"fr": "{module} n'est pas chargé ou n'existe pas.",
"it": "{module} non caricato o non esiste"
},
"unignoring_channel": {
"en": "No longer ignoring {module} in {channel}.",
"na'vi": "Oel stayawm {module}ur mì {channel} set.",
"cy": "Nid anwybyddu {module} yn {channel} mwyach.",
"nl": "{module} wordt niet meer genegeerd in {channel}.",
"de": "{module} in {channel} ist nicht länger ausgeschaltet.",
"fr": "{module} dans {channel} n'est plus ignoré à présent.",
"it": "{module} in {channel} non viene più ignorato."
},
"not_ignoring_channel": {
"en": "{module} wasn't being ignored in {channel}.",
"na'vi": "Oel stayawm {module}ur mì {channel} li.",
"cy": "Nid yw {module} yn cael ei anwybyddu yn {channel}.",
"nl": "{module} werd niet genegeerd in {channel}.",
"de": "{module} war in {channel} nicht ausgeschaltet.",
"fr": "{module} n'était pas ignoré dans {channel}.",
"it": "{module} non veniva ignorato in {channel}"
}
}

View File

@ -0,0 +1,65 @@
## imgur
Various imgur functionality.
### Description
Posts information on imgur links which are pasted into the channel and provides
functionality to generate a random imgur link.
### Config
#### imagelength: 5
Length of slugs generated by the random imgur functionality.
#### nsfwwarn: true
Warn that images generated by the ~ri command may be NSFW.
#### apikey
Key to use with the imgur API.
#### highscore: ricount
Quote category to use for a 'highscore;' used to run games with the web
/random page, by storing a highscore based on some arbitrary rule in the
chosen quote category (say, how far can you get before seeing a turtle). Then,
on the imgur random page you can press 'c' to see a countdown towards the last
stored value in the highscore quote category. If you beat the highscore, simply
add the winning score to the quote category.
### Commands
#### ~ri
Generate a random imgur image and post a link to it in the channel.
### API
#### getRandomImage(callback)
Generate a random imgur image by generating random slugs and then testing for
their existence until it finds one which exists (and hasn't been deleted).
Callback is given with two parameters, the URL of the generated image, and the
slug for the generated image.
#### getImageInfoString(slug, callback)
Return a string containing info about the image with the given slug from the
imgur API. Callback is called with one argument, the info string.
#### getImageInfo(slug, callback)
Return data from the imgur API on an image with the given slug. Callback is
called with one argument, the information returned by the API.
### Web
#### /imgur/random
A web page which loads a random image from imgur. You can press the space bar to
load a new image, and information about the images are shown on the top-left of
the page. You can press 'c' to view a highscore count (as documented above).
#### /imgur/stats
Show statistics on the total use of the imgur module.
### Hooks
#### link
Posts information about an imgur link when one is linked in the channel.

View File

@ -0,0 +1,14 @@
{
"dbKeys": [ "imgur" ],
"dependencies": [ "web", "api", "link" ],
"imagelength": 5,
"ricachelength": 7,
"nsfwwarn": true,
"apikey": "86fd3a8da348b65",
"highscore": "ricount",
"autoadd": {
"e49e686582ce3f60cb51d00c10924861": "facebookman",
"b11b634c74562bcd4e5d17b0d90987be": "raffleguy"
},
"outputPrefix": "\u00038imgur\u000f"
}

View File

@ -0,0 +1,330 @@
/**
* Module Name: imgur
* Description: Various imgur functionality
*/
var _ = require('underscore')._,
request = require('request'),
async = require('async'),
crypto = require('crypto'),
humanise = require('humanize');
var imgur = function(dbot) {
this.ApiRoot = 'https://api.imgur.com/3/';
this.ExcludeRes = [
{ 'w': 800, 'h': 600 },
{ 'w': 1024, 'h': 768 },
{ 'w': 1280, 'h': 768 },
{ 'w': 1280, 'h': 960 },
{ 'w': 1366, 'h': 768 },
{ 'w': 1600, 'h': 900 },
{ 'w': 1680, 'h': 1050 },
{ 'w': 1920, 'h': 1080 },
{ 'w': 1024, 'h': 640 }
];
this.riCache = [];
this.internalAPI = {
'infoString': function(imgData) {
info = '';
if(!_.isUndefined(imgData) && _.has(imgData, 'data') && !_.isUndefined(imgData.data.type)) {
imgData = imgData.data;
if(imgData.title) {
info += imgData.title + ' - ';
}
if(imgData.type) {
if(imgData.animated) {
info += 'an animated ' + imgData.type.split('/')[1] + ' with ';
} else {
info += 'a ' + imgData.type.split('/')[1] + ' with ';
}
} else {
info += 'an image with ';
}
info += imgData.views + ' views (';
info += imgData.width + 'x' + imgData.height + ')';
info += ' ('+humanise.filesize(imgData.size)+')';
}
return info;
}.bind(this),
'albumInfoString': function(albumData) {
var info = '';
if(!_.isUndefined(albumData) && _.has(albumData, 'data') && !_.isUndefined(albumData.data.id)) {
albumData = albumData.data;
if(albumData.title) {
info += albumData.title + ' - ';
}
if(albumData.description) {
info += albumData.description.split('\n')[0] + ' is ';
}
info += 'an album with ' + albumData.images_count + ' images ';
info += 'and ' + albumData.views + ' views';
if(albumData.nsfw) {
info += ' - NSFW';
}
}
return info;
}.bind(this),
'galleryInfoString': function(galData) {
var info = '';
if(!_.isUndefined(galData) && _.has(galData, 'data') && !_.isUndefined(galData.data.is_album)) {
if(galData.data.is_album === true) {
info = this.internalAPI.albumInfoString(galData);
} else {
info = this.internalAPI.infoString(galData);
}
}
return info;
}.bind(this)
};
this.api = {
'getRandomImage': function(callback) {
var random = function(len) {
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
return len ? chars.charAt(~~(Math.random()*chars.length)) + random(len-1) : "";
};
var ext = [ 'gif', 'png', 'jpg' ];
var testSlug = random(5);
var testUrl = 'http://i.imgur.com/' +
testSlug +
'.' + ext[_.random(0, ext.length - 1)],
fbman = false;
dbot.db.imgur.totalHttpRequests += 1;
request(testUrl, function(error, response, body) {
// 492 is body.length of a removed image
if(!error && response.statusCode == 200 && body.length != 492) {
dbot.db.imgur.totalImages += 1;
var hash = crypto.createHash('md5').update(body).digest("hex");
if(_.has(dbot.modules, 'quotes')){
// autoadd: {"abcdef": "facebookman"}
if(_.has(dbot.config.modules.imgur.autoadd,hash)){
fbman = true;
var category = this.config.autoadd[hash];
if (_.contains(category, testUrl)){
// there's probably less than 62^5 chance of this happening
} else {
dbot.api.quotes.addQuote(category, testUrl,
dbot.config.name, function() { });
}
}
}
callback(testUrl, testSlug, hash, fbman);
} else {
this.api.getRandomImage(callback);
}
}.bind(this));
},
'getGoodRandomImage': function(callback) {
this.api.getRandomImage(function(url, slug, hash, fbman) {
this.api.getImageInfo(slug, function(imgData) {
if(!_.isUndefined(imgData) &&
imgData.data &&
imgData.data.height > 500 && imgData.data.width > 500 &&
!_.any(this.ExcludeRes, function(res) {
return imgData.data.height == res.h && imgData.data.width == res.w;
})) {
callback(url, imgData);
} else if(fbman === true) {
callback(url, imgData);
} else {
this.api.getGoodRandomImage(callback);
}
}.bind(this));
}.bind(this));
},
'getImageInfoString': function(slug, callback) {
this.api.getImageInfo(slug, function(imgData) {
callback(this.internalAPI.infoString(imgData));
}.bind(this));
},
'getImageInfo': function(slug, callback) {
request.get({
'url': 'https://api.imgur.com/3/image/' + slug + '.json',
'json': true,
'headers': {
'Authorization': 'Client-ID ' + this.config.apikey
}
}, function(err, response, body) {
dbot.db.imgur.totalApiRequests += 1;
callback(body);
}.bind(this));
},
'getAlbumInfo': function(slug, callback) {
request.get({
'url': 'https://api.imgur.com/3/album/' + slug + '.json',
'json': true,
'headers': {
'Authorization': 'Client-ID ' + this.config.apikey
}
}, function(err, response, body) {
this.db.totalApiRequests += 1;
callback(body);
}.bind(this));
},
'getGalleryInfo': function(slug, callback) {
request.get({
'url': 'https://api.imgur.com/3/gallery/' + slug + '.json',
'json': true,
'headers': {
'Authorization': 'Client-ID ' + this.config.apikey
}
}, function(err, response, body) {
this.db.totalApiRequests += 1;
callback(body);
}.bind(this));
}
};
this.api['getRandomImage'].external = true;
this.api['getRandomImage'].extMap = [ 'callback' ];
this.api['getImageInfoString'].external = true;
this.api['getImageInfoString'].extMap = [ 'slug', 'callback' ];
this.commands = {
'~ri': function(event) {
var local = event.user;
if(event.params[1]) {
local = event.params.splice(1, event.params.length - 1).join(' ').trim();
}
var postImage = function(link, imgData) {
var info = this.internalAPI.infoString(imgData);
event.reply('['+this.config.outputPrefix + '] ' + local + ': ' + link + ' [' + info + ']');
}.bind(this);
var newCacheImage = function(link, imgData) {
this.riCache.push([link, imgData]);
}.bind(this);
var callback = postImage;
if(this.riCache.length > 0) {
var image = this.riCache.pop();
postImage(image[0], image[1]);
callback = newCacheImage;
}
this.api.getGoodRandomImage(callback);
},
// Legacy RI
'~lri': function(event) {
var local = event.user;
if(event.params[1]) {
local = event.params.splice(1, event.params.length - 1).join(' ').trim();
}
this.api.getRandomImage(function(link, slug) {
this.api.getImageInfo(slug, function(imgData) {
var info = this.internalAPI.infoString(imgData);
event.reply('['+this.config.outputPrefix + '] ' + local + ': ' + link + ' [' + info + ']');
}.bind(this));
}.bind(this));
},
// Super RI
'~sri': function(event) {
var local = event.user;
if(event.params[1]) {
local = event.params.splice(1, event.params.length - 1).join(' ').trim();
}
request.get({
'url': this.ApiRoot + 'gallery/random/random/',
'json': true,
'headers': {
'Authorization': 'Client-ID ' + this.config.apikey
}
}, function(err, response, body) {
if(!_.isUndefined(body) && body.data && body.data[0] != undefined) {
var num = _.random(0, body.data.length - 1);
this.api.getGalleryInfo(body.data[num].id, function(gal) {
event.reply('['+this.config.outputPrefix + '] ' + local + ': ' + gal.data.link + ' [' +
this.internalAPI.galleryInfoString(gal) + ']');
}.bind(this));
}
}.bind(this));
},
'~imgur': function(event) {
var term = event.input[1];
request.get({
'url': this.ApiRoot + 'gallery/search/',
'json': true,
'headers': {
'Authorization': 'Client-ID ' + this.config.apikey
},
'qs': {
'q': term
}
}, function(err, response, body) {
if(!_.isUndefined(body) && body.data && body.data[0] != undefined) {
var num = _.random(0, body.data.length - 1);
this.api.getGalleryInfo(body.data[num].id, function(gal) {
event.reply(dbot.t('imgurinfo', {
'info': this.internalAPI.galleryInfoString(gal)
}) + ' - ' + gal.data.link);
}.bind(this));
} else {
event.reply(dbot.t('imgur_noresults'));
}
}.bind(this));
}
}
this.commands['~imgur'].regex = [/^imgur ([\d\w\s-]*)/, 2];
this.onLoad = function() {
var imgurHandler = function(matches, name, callback) {
if(matches[1]) {
var dataCallback = function(data) {
var info;
if(name == 'imgurimage') {
info = this.internalAPI.infoString(data);
} else if(name == 'imguralbum') {
info = this.internalAPI.albumInfoString(data);
} else if(name == 'imgurgallery') {
info = this.internalAPI.galleryInfoString(data);
}
if(info) callback(dbot.t('imgurinfo', { 'info': info }));
}.bind(this);
if(name == 'imgurimage') {
this.api.getImageInfo(matches[1], dataCallback);
} else if(name == 'imguralbum') {
this.api.getAlbumInfo(matches[1], dataCallback);
} else if(name == 'imgurgallery') {
this.api.getGalleryInfo(matches[1], dataCallback);
}
}
}.bind(this);
dbot.api.link.addHandler('imguralbum', /https?:\/\/imgur\.com\/a\/([a-zA-Z0-9]+)/, imgurHandler);
dbot.api.link.addHandler('imgurgallery', /https?:\/\/imgur\.com\/gallery\/([a-zA-Z0-9]+)/, imgurHandler);
dbot.api.link.addHandler('imgurimage', /https?:\/\/i\.imgur\.com\/([a-zA-Z0-9]+)\.([jpg|png|gif])/, imgurHandler);
dbot.api.link.addHandler('imgurimage', /https?:\/\/imgur\.com\/([a-zA-Z0-9]+)/, imgurHandler);
async.times(this.config.ricachelength, function(n, next) {
this.api.getGoodRandomImage(function(link, imgData) {
this.riCache.push([ link, imgData ]);
next();
}.bind(this));
}.bind(this), function() {});
if(!_.has(dbot.db.imgur, 'totalHttpRequests')) dbot.db.imgur.totalHttpRequests = 0;
if(!_.has(dbot.db.imgur, 'totalApiRequests')) dbot.db.imgur.totalApiRequests = 0;
if(!_.has(dbot.db.imgur, 'totalImages')) dbot.db.imgur.totalImages = 0;
this.db = dbot.db.imgur;
}.bind(this);
};
exports.fetch = function(dbot) {
return new imgur(dbot);
}

View File

@ -0,0 +1,23 @@
var _ = require('underscore')._;
var pages = function(dbot) {
return {
'/imgur/random': function(req, res) {
var highScore = 0;
res.render('imgurr', { "highscore" : highScore });
},
'/imgur/stats': function(req, res) {
res.render('imgurstats', {
'name': dbot.config.name,
'totalHttpRequests': this.db.totalHttpRequests,
'totalApiRequests': this.db.totalApiRequests,
'totalImages': this.db.totalImages
});
}
};
};
exports.fetch = function(dbot) {
return pages(dbot);
};

View File

@ -0,0 +1,22 @@
{
"nsfw": {
"en": "might be NSFW",
"na'vi": "kxawm ke wivìntxu evengur",
"cy": "Gallai fod yn anniogel ar gwaith",
"nl": "bevat mogelijk gevoelige beelden",
"de": "Könnte 18+ Material enthalten",
"fr": "peut être risqué pour le travail (NSFW)",
"it": "può essere rischioso al lavoro (NSFW)"
},
"imgurinfo": {
"en": "[{info}]",
"de": "[{info}]",
"fr": "[{info}]",
"it": "[{info}]"
},
"imgur_noresults": {
"en": "No results found.",
"de": "Kein Suchergebnis.",
"it": "Nessun risultato"
}
}

View File

@ -0,0 +1,40 @@
## JS
Run JavaScript.
### Description
This module provides two commands which allow the execution of Javascript code
from the bot.
### Commands
#### ~js [code]
For regular users, there is the *~js* command, which is completely sandboxed,
but can still be used for calculation and the like.
> ~js Array(16).join('wat'-1) + " Batman!";
'NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN Batman!'
This feature is fairly safe as the user doesn't have access to anything
dangerous, and is safe from infinite loops or locking DBot up because the code
which is run is killed if it does not finish within a short amount of time.
#### ~ajs [code]
For administrators, the incredibly useful *~ajs* command is also available. The
input for this command is simply 'eval'-ed and therefore has full access to
DBot's memory. Of course, this is incredibly unsafe, but I find it rather fun;
remember to only give extremely trusted friends administrator access to your
DBot instance, as there's nothing to stop them wiping the database or probably
even your hard drive - if you're worried about that kind of thing, do not load
this module.
However, it's useful for many things, such as administrative activity for
which there isn't a command in the admin module. For example, you could hot-add
a new administrator like this:
> ~ajs dbot.admin.push('batman');
2
You can also use it for debugging, or even adding new commands while DBot is
running.

View File

@ -0,0 +1,9 @@
{
"commands": {
"~js": {
"disabled": true
}
},
"dependencies": [ "command" ],
"ignorable": true
}

51
modules-stock/js/js.js Normal file
View File

@ -0,0 +1,51 @@
/**
* Module Name: JS
* Description: Allows users to run sandboxed JS code, printing the result in
* the channel. Also allows admins to run un-sandboxed Javascript code with
* access to the DepressionBot instance memory.
*/
var vm = require('vm');
var sbox = require('sandbox');
var js = function(dbot) {
var commands = {
// Run JS code sandboxed, return result to channel.
'~js': function(event) {
try {
var s = new sbox();
s.run(event.input[1], function(output) {
event.reply(output.result);
}.bind(this));
} catch(err) {}
},
// Run JS code un-sandboxed, with access to DBot memory (admin-only).
'~ajs': function(event) {
var callback = function() {
var args = Array.prototype.slice.call(arguments);
for(var i=0;i<args.length;i++) {
var arg = args[i];
if(_.isObject(arg) && !_.isArray(arg)) {
arg = '[object Object]: ' + _.keys(arg).join(', ');
}
event.reply('Callback[' + i + ']: ' + arg);
}
};
var ret = eval(event.input[1]);
if(ret !== undefined) {
event.reply(ret);
}
}
};
commands['~js'].regex = [/^js (.*)/, 2];
commands['~ajs'].regex = [/^ajs (.*)/, 2];
commands['~ajs'].access = 'admin';
this.name = 'js';
this.ignorable = true;
this.commands = commands;
};
exports.fetch = function(dbot) {
return new js(dbot);
};

View File

@ -0,0 +1,4 @@
{
"~js": "~js [command]",
"~ajs": "~ajs [command]"
}

View File

@ -0,0 +1,5 @@
{
"announce": [ { "server": "tripsit", "name": "#dbot" } ],
"dbKeys": [ "karma" ],
"dbType": "redis"
}

View File

@ -0,0 +1,201 @@
/**
* Module Name: Karma
* Description: Thanking, with Karma!
*/
var _ = require('underscore')._;
var karma = function(dbot) {
this.lastKarma = {};
this.internalAPI = {
'getKarma': function(item, callback) {
this.db.read('karma', item.toLowerCase(), callback);
}.bind(this),
'setKarma': function(item, value, callback) {
this.db.save('karma', item.toLowerCase(), {
'item': item.toLowerCase(),
'karma': value
}, callback);
}.bind(this)
};
this.commands = {
'karma': function(event) {
var item = event.params[1] || event.user;
this.internalAPI.getKarma(item, function(err, karma) {
if(!err && karma) {
karma = karma.karma;
} else {
karma = 0;
}
event.reply(dbot.t('karma', {
'item': item,
'karma': karma
}));
});
},
'setkarma': function(event) {
var item = event.params[1],
value = parseInt(event.params[2]);
this.internalAPI.setKarma(item, value, function(err, karma) {
event.reply(dbot.t('newkarma', {
'item': item,
'value': value
}));
});
},
'unkarmaiest': function(event) {
var karmas = {};
this.db.scan('karma', function(karma) {
if(karma && !_.isUndefined(karma.item)) {
karmas[karma.item] = karma.karma;
}
}.bind(this), function(err) {
var qSizes = _.chain(karmas)
.pairs()
.sortBy(function(category) { return category[1]; })
.first(10)
.value();
var qString = 'Unkarmaiest: ';
for(var i=0;i<qSizes.length;i++) {
qString += qSizes[i][0] + " (" + qSizes[i][1] + "), ";
}
event.reply(qString.slice(0, -2));
});
},
'karmaiest': function(event) {
var karmas = {};
this.db.scan('karma', function(karma) {
if(karma && !_.isUndefined(karma.item)) {
karmas[karma.item] = karma.karma;
}
}.bind(this), function(err) {
var qSizes = _.chain(karmas)
.pairs()
.sortBy(function(category) { return category[1]; })
.reverse()
.first(10)
.value();
var qString = 'Karmaiest: ';
for(var i=0;i<qSizes.length;i++) {
qString += qSizes[i][0] + " (" + qSizes[i][1] + "), ";
}
event.reply(qString.slice(0, -2));
});
},
'wattest': function(event) {
var karmas = {};
this.db.scan('karma', function(karma) {
if(karma && !_.isUndefined(karma.item)) {
if(karma.item.match(/_wat$/)) {
karmas[karma.item] = karma.karma;
}
}
}.bind(this), function(err) {
var qSizes = _.chain(karmas)
.pairs()
.sortBy(function(category) { return category[1]; })
.reverse()
.first(10)
.value();
var qString = 'Karmaiest: ';
for(var i=0;i<qSizes.length;i++) {
qString += qSizes[i][0] + " (" + qSizes[i][1] + "), ";
}
event.reply(qString.slice(0, -2));
});
}
};
this.commands.setkarma.access = 'admin';
this.listener = function(event) {
dbot.api.ignore.isUserBanned(event.rUser, 'karma', function(isBanned) {
if(!isBanned) {
var match = event.message.match(/^(.+)(\+\+|\-\-)$/);
if(event.user !== dbot.config.name && match && match[1].length < 25) {
match[1] = match[1].replace(/(\+|\-)/g,'').replace(/:/g,'').trim();
var timeout = 5000;
/* if(event.channel.name == '#stims' || event.channel.name == '##meth' || event.channel.name == '##sweden') {
timeout = 20000;
}*/
if(_.has(this.lastKarma, event.rUser.id) && this.lastKarma[event.rUser.id]+ timeout > Date.now()) {
return event.reply('Try again in a few seconds : - )');
} else if(event.rUser.currentNick.toLowerCase() === match[1].toLowerCase() || event.rUser.primaryNick.toLowerCase() === match[1].toLowerCase()) {
return event.reply('Stop playing with yourself : - )');
} else if(event.channel == event.user) {
return event.reply('Don\'t be a Secretive Sally : - )');
}
if(event.channel.name == '##wat') {
match[1] = match[1].replace(/_wat$/, '');
match[1] += '_wat';
}
this.internalAPI.getKarma(match[1], function(err, karma) {
if(!karma) {
karma = 0;
} else {
karma = karma.karma;
}
if(match[2] === '--') {
if(match[1].toLowerCase() =='weed') {
karma -= 2;
} else {
karma -= 1;
}
} else {
if(match[1].toLowerCase() == 'weed') {
karma += 2;
} else {
karma += 1;
}
}
this.internalAPI.setKarma(match[1], karma, function(err, karma) {
this.lastKarma[event.rUser.id] = Date.now();
var pre;
if(karma.karma > 0) {
pre = '[\u00039karma\u000f]';
karma.karma = '\u00039 '+karma.karma+'\u000f';
} else if(karma.karma < 0) {
pre = '[\u00034karma\u000f]';
karma.karma = '\u00034 '+karma.karma+'\u000f';
} else {
pre = '[\u00036karma\u000f]';
karma.karma = '\u00036 '+karma.karma+'\u000f';
}
event.reply(pre + ' ' + dbot.t('newkarma', {
'item': match[1],
'value': karma.karma
}));
if(_.has(dbot.modules, 'log')) {
dbot.api.log.logWithChannel(event.server, event.channel, event.rUser.primaryNick, event.message);
}
}.bind(this));
}.bind(this));
}
}
}.bind(this));
}.bind(this);
this.on = 'PRIVMSG';
};
exports.fetch = function(dbot) {
return new karma(dbot);
};

View File

@ -0,0 +1,8 @@
{
"karma": {
"en": "[karma] {item} has {karma} karma"
},
"newkarma": {
"en": "{item} now has{value} karma"
}
}

View File

@ -0,0 +1,29 @@
## Kick
Kicking and kicking-related accessories.
### Description
This module counts the number of times people are kicked from and kick people
from channels, and provides commands for viewing this data. It also has the bot
attempt to rejoin the channel if it is kicked.
### Commands
#### ~kickcount [username]
Show the number of times a given user has been kicked and has kicked other
people.
#### ~kickstats
Show a list of top kickers and kickees.
#### ~ckick [#channel] [username] [reason]
Kick a user from a channel.
#### ~cban [#channel] [username] [reason]
Ban a user from a channel.
#### ~nban {optional: Duration in Hours} [username] [reason]
Ban a user from the network.
#### ~nunban [username] [reason]
Unban a user from the network.

View File

@ -0,0 +1,512 @@
var _ = require('underscore')._,
uuid = require('node-uuid');
var commands = function (dbot) {
var commands = {
/*** Kick Management ***/
'~quiet': function (event) {
var server = event.server,
quieter = event.rUser,
duration = event.input[1],
channel = (event.input[2] || event.channel.name).trim(),
quietee = event.input[3].trim(),
reason = event.input[4] || "N/A";
this.api.quietUser(server, quieter, duration, channel, quietee, reason, function (response) {
event.reply(response);
});
},
'~timeout': function (event) {
var server = event.server,
quieter = event.rUser,
duration = this.config.timeoutTime,
channel = event.channel.name,
quietee = event.input[1],
reason = event.input[2] || "N/A";
reason += ' #timeout';
dbot.api.users.resolveUser(server, quietee, function (err, user) {
if (!err && user) {
if (!_.has(this.recentTimeouts, user.id)) {
this.recentTimeouts[user.id] = 0;
}
this.recentTimeouts[user.id] += 1;
setTimeout(function () {
this.recentTimeouts[user.id] -= 1;
if (this.recentTimeouts[user.id] == 0) {
delete this.recentTimeouts[user.id];
}
}
.bind(this), 3600000);
if (this.recentTimeouts[user.id] == 3) {
duration = null;
reason += ' #permatimeout';
dbot.say(event.server, dbot.config.servers[event.server].admin_channel, quietee + ' has been given three timeouts in the last hour, and so has been quieted indefinitely in ' + channel + '. Please review.');
}
this.api.quietUser(server, quieter, duration, channel, quietee, reason, function (response) {
event.reply(response);
});
}
}
.bind(this));
},
'~unquiet': function (event) {
var server = event.server,
quieter = event.user,
channel = (event.input[1] || event.channel.name).trim(),
quietee = event.input[2].trim();
if (_.has(this.hosts[server], quietee)) {
if (_.include(this.config.quietBans, channel)) {
this.api.unban(server, this.hosts[server][quietee], channel);
} else {
this.api.unquiet(server, this.hosts[server][quietee], channel);
}
event.reply(dbot.t('unquieted', {
'quietee': quietee
}));
dbot.api.report.notify('unquiet', server, event.rUser, channel,
dbot.t('unquiet_notify', {
'unquieter': quieter,
'quietee': quietee
}), false, quietee);
}
},
'~ckick': function (event) {
var server = event.server,
kicker = event.user,
kickee = event.input[2],
channel = event.input[1],
reason = event.input[3];
if (_.isUndefined(channel)) {
channel = event.channel.name;
}
channel = channel.trim();
this.api.kick(server, kickee, channel, reason + ' (requested by ' + kicker + ')');
dbot.api.report.notify('kick', server, event.rUser, channel, dbot.t('ckicked', {
'kicker': kicker,
'kickee': kickee,
'reason': reason
}), false, kickee);
},
// Kick and ban from all channels on the network.
'~nban': function (event) {
if (!event.input)
return;
var server = event.server,
banner = event.user,
timeout = event.input[1],
banee = event.input[2],
reason = event.input[3],
adminChannel = dbot.config.servers[server].admin_channel,
channels = _.keys(dbot.instance.connections[server].channels),
network = event.server;
if (this.config.network_name[event.server]) {
network = this.config.network_name[event.server];
}
dbot.api.nickserv.getUserHost(event.server, banee, function (host) {
// Add host record entry
if (host) {
var didKill = false;
if ((reason.match('#line') || reason.match('#specialk') || reason.match('#kline')) && _.include(dbot.access.moderator(), event.rUser.primaryNick)) {
didKill = true;
var t = ' !P ';
if (timeout) {
t = ' !T ' + timeout + ' ';
}
dbot.say(event.server, 'operserv', 'akill add ' + banee + t + banee + ' banned by ' + banner + ': ' + reason);
}
// Do not ban if user was killed - redundant
if(!didKill) {
// Ban from current channel first
this.api.ban(server, host, event.channel);
this.api.kick(server, banee, event.channel, reason +
' (network-wide ban)');
channels = _.without(channels, event.channel);
if (!_.isUndefined(adminChannel)) {
channels = _.without(channels, adminChannel);
} else {
adminChannel = event.channel.name;
}
// Ban the user from all channels
var i = 0;
var banChannel = function (channels) {
if (i >= channels.length)
return;
var channel = channels[i];
this.api.ban(server, host, channel);
this.api.kick(server, banee, channel, reason +
' (network-wide ban)');
i++;
banChannel(channels);
}
.bind(this);
banChannel(channels);
}
this.hosts[event.server][banee] = host;
// Create notify string
if (!_.isUndefined(timeout)) {
timeout = timeout.trim();
var msTimeout = new Date(new Date().getTime() + (parseFloat(timeout) * 3600000));
if (_.has(dbot.modules, 'remind')) {
msTimeout = dbot.api.remind.parseTime(timeout);
if (!msTimeout) {
return event.reply('Invalid time. Remember you must give e.g. 5m now.');
}
timeout = timeout.replace(/([\d]+)d/, '$1 days').replace(/([\d]+)h/, '$1 hours ').replace(/([\d]+)m/, '$1 minutes ').replace(/([\d]+)s/, '$1 seconds').trim();
} else {
timeout += ' hours';
}
// Do not schedule unbans if the user was killed as no ban was put in place
if(!didKill) {
if (!_.has(this.tempBans, event.server))
this.tempBans[event.server] = {};
this.tempBans[event.server][banee] = msTimeout;
this.internalAPI.addTempBan(event.server, banee, msTimeout);
}
var notifyString = dbot.t('tbanned', {
'network': network,
'banner': banner,
'banee': banee,
'hours': timeout,
'host': host,
'reason': reason
});
} else {
var notifyString = dbot.t('nbanned', {
'network': network,
'banner': banner,
'banee': banee,
'host': host,
'reason': reason
});
}
// Add db entry documenting ban
if (this.config.document_bans) {
var id = uuid.v4();
var banRecord = {
'id': id,
'time': new Date().getTime(),
'server': server,
'banee': banee,
'banner': banner,
'host': host,
'reason': reason
};
this.db.save('nbans', id, banRecord, function () {});
}
// Notify moderators, banee
if (!_.isUndefined(adminChannel)) {
channels = _.without(channels, adminChannel);
} else {
adminChannel = event.channel.name;
}
dbot.api.report.notify('ban', server, event.rUser, adminChannel, notifyString, false, banee);
dbot.say(event.server, adminChannel, notifyString);
if (!_.isUndefined(timeout)) {
dbot.say(event.server, banee, dbot.t('tbanned_notify', {
'network': network,
'banner': banner,
'reason': reason,
'hours': timeout,
'admin_channel': adminChannel
}));
} else {
dbot.say(event.server, banee, dbot.t('nbanned_notify', {
'network': network,
'banner': banner,
'reason': reason,
'hours': timeout,
'admin_channel': adminChannel
}));
}
// err
dbot.say(event.server, 'NickServ', 'FREEZE ' + banee + ' ON ' + reason);
} else {
event.reply(dbot.t('no_user', {
'user': banee
}));
}
}
.bind(this));
},
'~nunban': function (event) {
var unbanee = event.params[1],
host = event.params[2] || undefined,
unbanner = event.rUser;
this.api.networkUnban(event.server, unbanee, unbanner, host, function (err) {
if (err) {
event.reply(dbot.t('nunban_error', {
'unbanee': unbanee
}));
}
});
},
/*** Kick Stats ***/
// Give the number of times a given user has been kicked and has kicked
// other people.
'~kickcount': function (event) {
var username = event.params[1];
if (!_.has(dbot.db.kicks, username)) {
var kicks = '0';
} else {
var kicks = dbot.db.kicks[username];
}
if (!_.has(dbot.db.kickers, username)) {
var kicked = '0';
} else {
var kicked = dbot.db.kickers[username];
}
event.reply(dbot.t('user_kicks', {
'user': username,
'kicks': kicks,
'kicked': kicked
}));
},
// Output a list of the people who have been kicked the most and those
// who have kicked other people the most.
'~kickstats': function (event) {
var orderedKickLeague = function (list, topWhat) {
var kickArr = _.chain(list)
.pairs()
.sortBy(function (kick) {
return kick[1]
})
.reverse()
.first(10)
.value();
var kickString = "Top " + topWhat + ": ";
for (var i = 0; i < kickArr.length; i++) {
kickString += kickArr[i][0] + " (" + kickArr[i][1] + "), ";
}
return kickString.slice(0, -2);
};
event.reply(orderedKickLeague(dbot.db.kicks, 'Kicked'));
event.reply(orderedKickLeague(dbot.db.kickers, 'Kickers'));
},
'~votequiet': function (event) {
var target = event.input[1],
reason = event.input[2];
if (_.has(event.channel.nicks, target)) {
dbot.api.users.resolveUser(event.server, target, function (err, user) {
if (!err && user) {
if (_.include(dbot.access.power_user(), user.primaryNick) || target == dbot.config.name) {
return event.reply('User is immune to votequiet.');
}
if (!_.has(this.voteQuiets, user.id)) {
this.voteQuiets[user.id] = {
'user': user.id,
'reason': reason,
'channel': event.channel,
'yes': [event.rUser.primaryNick],
'no': []
};
event.reply(event.user + ' has started a vote to quiet ' + target + ' for "' + reason + '." Type either "~voteyes ' + target + '" or "~voteno ' + target + '" in the next 90 seconds.');
this.voteQuiets[user.id].timer = setTimeout(function () {
var vq = this.voteQuiets[user.id];
vq.spent = true;
if (vq.yes.length >= 3 && vq.no.length < 2) {
event.reply('Attempt to quiet ' + target + ' succeeded. Count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
this.api.quietUser(event.server, event.rUser, '10m', event.channel, target, reason + '[votequiet]', function (response) {
clearTimeout(vq.timer);
event.reply(response);
});
} else {
event.reply('Attempt to quiet ' + target + ' failed. Count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
}
var nString = 'A votequiet was attempted on ' + target + ' in ' + event.channel + '. It was initiated by ' + event.rUser.primaryNick + '. ' +
vq.yes.join(', ') + ' voted yes (' + vq.yes.length + '). ';
if (vq.no.length > 0) {
nString += vq.no.join(', ') + ' voted no (' + vq.no.length + ').'
}
dbot.api.report.notify('votequiet', event.server, event.rUser, event.channel, nString, false, target);
setTimeout(function () {
delete this.voteQuiets[user.id];
}
.bind(this), 600000);
}
.bind(this), 90000);
} else {
if (this.voteQuiets[user.id].spent) {
event.reply('A votequiet attempt has already been made on this user in the last 10 minutes.');
} else {
var vq = this.voteQuiets[user.id]
if (!_.include(vq.yes, event.rUser.primaryNick)) {
vq.yes.push(event.rUser.primaryNick);
event.reply('There is already a votequiet attempt active for this user, adding yes vote to existing poll.');
event.reply('Voted yes on votequiet for ' + target + '. New count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
if (vq.yes.length == 4) {
event.reply('Attempt to quiet ' + target + ' succeeded. Count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
this.api.quietUser(event.server, event.rUser, '10m', event.channel, target, reason + '[votequiet]', function (response) {
clearTimeout(vq.timer);
vq.spent = true;
setTimeout(function () {
delete this.voteQuiets[user.id];
}
.bind(this), 600000);
event.reply(response);
});
}
} else {
event.reply('There is already a votequiet attempt active for this user, and you already voted yes!');
}
}
}
} else {
event.reply('Target does not seem to be in the channel.');
}
}
.bind(this));
} else {
event.reply('Target does not seem to be in the channel.');
}
},
'~voteyes': function (event) {
var target = event.params[1];
dbot.api.users.resolveUser(event.server, target, function (err, user) {
if (!err && user) {
if (user.id == event.rUser.id) {
return event.reply('You cannot vote on your own silencing. Be good.');
}
if (_.has(this.voteQuiets, user.id) && !this.voteQuiets[user.id].spent) {
var vq = this.voteQuiets[user.id];
if (event.channel != vq.channel) {
return event.reply('Vote must be in ' + vq.channel);
}
if (!_.include(vq.yes, event.rUser.primaryNick) && !_.include(vq.no, event.rUser.primaryNick)) {
vq.yes.push(event.rUser.primaryNick);
event.reply('Voted yes on votequiet for ' + target + '. New count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
if (vq.yes.length == 4) {
event.reply('Attempt to quiet ' + target + ' succeeded. Count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
this.api.quietUser(event.server, event.rUser, '10m', event.channel, target, vq.reason + '[votequiet]', function (response) {
clearTimeout(vq.timer);
vq.spent = true;
setTimeout(function () {
delete this.voteQuiets[user.id];
}
.bind(this), 600000);
event.reply(response);
});
}
} else {
event.reply('You have already voted.');
}
} else {
event.reply('There is no active votequiet for this user. You can start one by typing "~votequiet ' + target + ' [reason].');
}
} else {
event.reply('No idea who that is m8');
}
}
.bind(this));
},
'~voteno': function (event) {
var target = event.params[1];
dbot.api.users.resolveUser(event.server, target, function (err, user) {
if (!err && user) {
if (user.id == event.rUser.id) {
return event.reply('You cannot vote on your own silencing. Be good.');
}
if (_.has(this.voteQuiets, user.id) && !this.voteQuiets[user.id].spent) {
var vq = this.voteQuiets[user.id];
if (event.channel != vq.channel) {
return event.reply('Vote must be in ' + vq.channel);
}
if (!_.include(vq.yes, event.rUser.primaryNick) && !_.include(vq.no, event.rUser.primaryNick)) {
vq.no.push(event.rUser.primaryNick);
event.reply('Voted no on votequiet for ' + target + '. New count: Yes (' + vq.yes.length + '). No (' + vq.no.length + ').');
} else {
event.reply('You have already voted.');
}
} else {
event.reply('There is no active votequiet for this user. You can start one by typing "~votequiet ' + target + ' [reason].');
}
} else {
event.reply('No idea who that is m8');
}
}
.bind(this));
}
};
_.each(commands, function (command) {
command.access = 'moderator';
});
commands['~kickcount'].access = 'regular';
commands['~kickstats'].access = 'regular';
commands['~votequiet'].access = 'regular';
commands['~voteyes'].access = 'regular';
commands['~voteno'].access = 'regular';
commands['~quiet'].access = 'voice';
commands['~timeout'].access = 'voice';
commands['~unquiet'].access = 'voice';
commands['~nban'].access = 'power_user';
commands['~nunban'].access = 'power_user';
commands['~ckick'].regex = /^ckick (#[^ ]+ )?([^ ]+) ?(.*)?$/;
commands['~nban'].regex = /^nban (\d[\d\.dhmsy]+)? ?([^ ]+) (.+)$/;
commands['~quiet'].regex = /^quiet (\d[\d\.hmsy]+)? ?(#[^ ]+ )?([^ ]+) ?(.*)?$/;
commands['~timeout'].regex = /^timeout ([^ ]+) ?(.*)?$/;
commands['~unquiet'].regex = /^unquiet (#[^ ]+ )?([^ ]+) ?$/;
commands['~votequiet'].regex = [/^votequiet ([^ ]+) (.+)$/, 3];
return commands;
};
exports.fetch = function (dbot) {
return commands(dbot);
};

View File

@ -0,0 +1,18 @@
{
"dbKeys": [ "kicks", "kickers", "hosts", "tempBans" ],
"dependencies": [ "command", "report", "users" ],
"ignorable": true,
"countSilently": true,
"quietBans": [
"#wherever"
],
"network_name": {
"aberwiki": "OAOSIDL"
},
"chanserv": "ChanServ",
"document_bans": false,
"dbType": "redis",
"requireWebLogin": true,
"webAccess": "power_user",
"timeoutTime": "10m"
}

270
modules-stock/kick/kick.js Normal file
View File

@ -0,0 +1,270 @@
var _ = require('underscore')._;
var kick = function (dbot) {
if (!_.has(dbot.db, 'recentTimeouts')) {
dbot.db.recentTimeouts = {};
}
this.recentTimeouts = dbot.db.recentTimeouts;
this.api = {
'ban': function (server, host, channel) {
dbot.instance.connections[server].send('MODE ' + channel + ' +b *!*@' + host);
},
'quiet': function (server, host, channel) {
dbot.instance.connections[server].send('MODE ' + channel + ' +q *!*@' + host);
},
'unquiet': function (server, host, channel) {
dbot.instance.connections[server].send('MODE ' + channel + ' -q *!*@' + host);
},
'devoice': function (server, nick, channel) {
dbot.instance.connections[server].send('MODE ' + channel + ' -v ' + nick);
},
'voice': function (server, nick, channel) {
dbot.instance.connections[server].send('MODE ' + channel + ' +v ' + nick);
},
'kick': function (server, user, channel, msg) {
dbot.instance.connections[server].send('KICK ' + channel + ' ' + user + ' :' + msg);
},
'kill': function (server, user, reason) {
dbot.instance.connections[server].send('kill ' + user + ' ' + reason);
},
'unban': function (server, host, channel) {
// TODO: Wrest control from chanserv
//dbot.say(server, this.config.chanserv, 'unban ' + channel + ' *!*@' + host);
dbot.instance.connections[server].send('MODE ' + channel + ' -b *!*@' + host);
},
'quietUser': function (server, quieter, duration, channel, quietee, reason, callback) {
dbot.api.nickserv.getUserHost(server, quietee, function (host) {
// Add host record entry
if (host) {
this.hosts[server][quietee] = host;
if (!_.isUndefined(duration) && !_.isNull(duration)) {
duration = duration.trim();
var msTimeout = new Date(new Date().getTime() + (parseFloat(duration) * 60000));
if (_.has(dbot.modules, 'remind')) {
msTimeout = dbot.api.remind.parseTime(duration);
if (!msTimeout) {
return callback('Invalid time. Remember you must give e.g. 5m now.');
}
duration = duration.replace(/([\d]+)d/, '$1 years').replace(/([\d]+)d/, '$1 days').replace(/([\d]+)h/, '$1 hours ').replace(/([\d]+)m/, '$1 minutes ').replace(/([\d]+)s/, '$1 seconds').trim();
} else {
duration += ' minutes';
}
var vStatus = dbot.instance.connections[server].channels[channel].nicks[quietee].voice;
dbot.api.timers.addTimeout(msTimeout, function () {
if (_.has(this.hosts[server], quietee)) {
if (_.include(this.config.quietBans, channel)) {
this.api.unban(server, this.hosts[server][quietee], channel);
this.api.voice(server, quietee, channel);
} else {
this.api.unquiet(server, this.hosts[server][quietee], channel);
}
dbot.api.users.resolveUser(server, dbot.config.name, function (err, user) {
dbot.api.report.notify('unquiet', server, user, channel,
dbot.t('unquiet_notify', {
'unquieter': dbot.config.name,
'quietee': quietee
}), false, quietee);
});
}
}
.bind(this));
callback(dbot.t('tquieted', {
'quietee': quietee,
'minutes': duration
}));
dbot.api.report.notify('quiet', server, quieter.primaryNick, channel,
dbot.t('tquiet_notify', {
'minutes': duration,
'quieter': quieter.primaryNick,
'quietee': quietee,
'reason': reason
}), false, quietee);
} else {
callback(dbot.t('quieted', {
'quietee': quietee
}));
dbot.api.report.notify('quiet', server, quieter.primaryNick, channel,
dbot.t('quiet_notify', {
'quieter': quieter.primaryNick,
'quietee': quietee,
'reason': reason
}), false, quietee);
}
this.api.devoice(server, quietee, channel);
if (_.include(this.config.quietBans, channel)) {
this.api.ban(server, this.hosts[server][quietee], channel);
} else {
this.api.quiet(server, host, channel);
}
if (reason.indexOf('#warn') !== -1) {
dbot.api.warning.warn(server, quieter, quietee,
'Quieted in ' + channel + ' for ' + reason, channel,
function () {});
}
} else {
event.reply(dbot.t('no_user', {
'user': quietee
}));
}
}
.bind(this));
},
'networkUnban': function (server, unbanee, unbanner, manualHost, callback) {
var channels = dbot.config.servers[server].channels,
network = this.config.network_name[server] || server,
adminChannel = dbot.config.servers[server].admin_channel;
if (!_.isUndefined(manualHost)) {
this.hosts[server][unbanee] = manualHost;
}
if (_.has(this.hosts, server) && _.has(this.hosts[server], unbanee) && _.isString(this.hosts[server][unbanee])) {
var host = this.hosts[server][unbanee];
// Notify Staff
if (_.isUndefined(adminChannel)) {
adminChannel = event.channel.name;
}
var notifyString = dbot.t('nunbanned', {
'network': network,
'unbanee': unbanee,
'host': host,
'unbanner': unbanner.currentNick
});
dbot.api.report.notify('unban', server, unbanner, adminChannel, notifyString, false, unbanee);
dbot.say(server, adminChannel, notifyString);
// Notify Unbanee
dbot.say(server, unbanee, dbot.t('nunban_notify', {
'network': network,
'unbanee': unbanee,
'unbanner': unbanner.currentNick
}));
// Unban
var i = 0;
var unbanChannel = function (channels) {
if (i >= channels.length)
return;
var channel = channels[i];
this.api.unban(server, host, channel);
setTimeout(function () {
i++;
unbanChannel(channels);
}, 1000);
}
.bind(this);
unbanChannel(channels);
dbot.say(server, 'NickServ', 'FREEZE ' + unbanee + ' OFF');
callback(null); // Success
} else {
// Attempt to look up the host on-the-fly
dbot.api.nickserv.getUserHost(server, unbanee, unbanner, function (host) {
if (host) {
if (!_.has(this.hosts, server))
this.hosts[server] = {};
this.hosts[server][unbanee] = host;
this.api.networkUnban(server, unbanee, unbanner);
} else {
callback(true); // No host could be found
}
}
.bind(this));
}
}
};
this.internalAPI = {
'addTempBan': function (server, banee, timeout) {
dbot.api.users.resolveUser(server, dbot.config.name, function (err, bot) {
dbot.api.timers.addTimeout(timeout, function () {
this.api.networkUnban(server, banee, bot, undefined, function (err) {});
delete this.tempBans[server][banee];
}
.bind(this));
}
.bind(this));
}
.bind(this)
};
this.listener = function (event) {
if (event.kickee == dbot.config.name) {
dbot.instance.join(event, event.channel.name);
event.reply(dbot.t('kicked_dbot', {
'botname': dbot.config.name
}));
dbot.db.kicks[dbot.config.name] += 1;
} else {
if (!_.has(dbot.db.kicks, event.kickee)) {
dbot.db.kicks[event.kickee] = 1;
} else {
dbot.db.kicks[event.kickee] += 1;
}
if (!_.has(dbot.db.kickers, event.user)) {
dbot.db.kickers[event.user] = 1;
} else {
dbot.db.kickers[event.user] += 1;
}
if (!this.config.countSilently) {
event.reply(event.kickee + '-- (' + dbot.t('user_kicks', {
'user': event.kickee,
'kicks': dbot.db.kicks[event.kickee],
'kicked': dbot.db.kickers[event.kickee]
}) + ')');
}
}
}
.bind(this);
this.on = 'KICK';
this.onLoad = function () {
if (!_.has(dbot.db, 'hosts')) {
dbot.db.hosts = {};
_.each(dbot.config.servers, function (v, k) {
dbot.db.hosts[k] = {};
}, this);
}
if (!_.has(dbot.db, 'tempBans'))
dbot.db.tempBans = {};
this.hosts = dbot.db.hosts;
this.tempBans = dbot.db.tempBans;
this.voteQuiets = {};
_.each(this.tempBans, function (bans, server) {
_.each(bans, function (timeout, nick) {
timeout = new Date(timeout);
this.internalAPI.addTempBan(server, nick, timeout);
}, this);
}, this);
if (_.has(dbot.modules, 'web')) {
dbot.api.web.addIndexLink('/bans', 'Ban List');
}
}
.bind(this);
};
exports.fetch = function (dbot) {
return new kick(dbot);
};

View File

@ -0,0 +1,46 @@
var _ = require('underscore')._;
var pages = function (dbot) {
return {
'/bans': function (req, res) {
res.render('servers', {
'servers': _.keys(dbot.config.servers)
});
},
'/underbans': function (req, res) {
this.db.search('nbans', {
'server': server
}, function (ban) {
if (ban.reason.match('#underban')) {
bans.push(ban);
}
}, function () {
res.render('bans', {
'server': server,
'bans': bans
});
});
},
'/bans/:server': function (req, res) {
var server = req.params.server,
bans = [];
this.db.search('nbans', {
'server': server
}, function (ban) {
bans.push(ban);
}, function () {
res.render('bans', {
'server': server,
'bans': bans
});
});
}
}
};
exports.fetch = function (dbot) {
return pages(dbot);
};

Some files were not shown because too many files have changed in this diff Show More