Compare commits
714 Commits
feature/sn
...
ChaosChemn
Author | SHA1 | Date |
---|---|---|
ChChBot admin | 358b415566 | |
ChChBot admin | 3e1ea6ac4e | |
ChChBot admin | 48b627ad4b | |
ChChBot admin | 1f1ff649c9 | |
Florian Schlegel | 8344fe7693 | |
Florian Schlegel | a6992d5d1a | |
ChChBot admin | 21db4efd98 | |
ChChBot admin | 8941d59dc2 | |
Florian Schlegel | 09ce708709 | |
ChChBot admin | 29cc95f0ad | |
Florian Schlegel | 4a02f545ea | |
ChChBot admin | 28176ad2ad | |
ChChBot admin | 651d4a953e | |
ChChBot admin | 80711e1278 | |
Florian Schlegel | a88d5ef0bd | |
Markus Schmidl | 827c5c73e8 | |
ChChBot admin | 580415ed76 | |
Florian Schlegel | c562a2c7ae | |
ChChBot admin | 4b5e86c237 | |
txt.file | 11687a9ad0 | |
Markus Schmidl | 296de07e14 | |
Markus Schmidl | dfbead6946 | |
root | e30eca5ebd | |
root | 2a3c9f52dc | |
ChChBot admin | f88c3267d4 | |
ChChBot admin | 2b18eaac5a | |
Florian Schlegel | 0650046d18 | |
Michael Stummvoll | 4e386c9008 | |
Michael Stummvoll | 05bfb9099a | |
Michael Stummvoll | bfa3fb6e74 | |
Florian Schlegel | d415d0fc69 | |
Florian Schlegel | 433d97d276 | |
Florian Schlegel | 121715115a | |
Michael Stummvoll | 620de651ce | |
ChChBot admin | 3f205eaafa | |
ChChBot admin | 59cd8380d2 | |
ChChBot admin | 7cce9bf27e | |
ChChBot admin | 0ba2001b62 | |
Morris Jobke | 63fc042027 | |
Luke Rogers | 9421d8160d | |
Dabo Ross | 4b4ac2d918 | |
Luke Rogers | 05a68faf1d | |
Luke Rogers | 40328cf24f | |
Luke Rogers | fb471bee17 | |
Luke Rogers | 48ab7417b8 | |
Luke Rogers | 97a3283eff | |
Luke Rogers | 6d147e1677 | |
Luke Rogers | c99c44616c | |
Luke Rogers | 1062e69e56 | |
Luke Rogers | 5ca45fea4b | |
Luke Rogers | 04af154c5b | |
Luke Rogers | f82041fc87 | |
Luke Rogers | a027bd780f | |
Luke Rogers | a611e0df45 | |
Luke Rogers | e49ec9a9c9 | |
Luke Rogers | 5ee62107f9 | |
Dabo Ross | 4652ed90a3 | |
Dabo Ross | ff3ef576b7 | |
Luke Rogers | ec9f5e9777 | |
Luke Rogers | 7f3b433209 | |
Luke Rogers | 2cb772674a | |
Luke Rogers | e46cb5c826 | |
prplz | b917b495f7 | |
Luke Rogers | 4b7c8ffa75 | |
xxyy | 7cf3d88c96 | |
Luke Rogers | 21d06ae4f0 | |
Luke Rogers | 50e2f72be4 | |
xxyy | 1aacd8c511 | |
Luke Rogers | 6518fe7136 | |
Luke Rogers | fd7253ded7 | |
Luke Rogers | f05a35dd96 | |
Luke Rogers | 8340f55adf | |
Luke Rogers | 10907bf214 | |
Luke Rogers | d834ae9070 | |
Luke Rogers | 31d9031dcc | |
Luke Rogers | 17b75a2ba9 | |
Luke Rogers | 0acb22e0a7 | |
Luke Rogers | 1be204236a | |
Luke Rogers | a7a6f8c01e | |
Luke Rogers | 1bcf687ea2 | |
Luke Rogers | b9bf09e6b6 | |
Luke Rogers | 914bc255ab | |
Luke Rogers | 2028075d2f | |
Luke Rogers | 8b1de13c8e | |
Luke Rogers | d32619063a | |
Luke Rogers | 9cfdc0b706 | |
Luke Rogers | 5d3280b9d5 | |
Luke Rogers | 807cbf3f07 | |
Luke Rogers | c7d2535098 | |
Luke Rogers | 44cb335bc0 | |
Luke Rogers | 90a91c1ef8 | |
Luke Rogers | 13df70da1b | |
Luke Rogers | 5008da1331 | |
Luke Rogers | 281a77c67c | |
Luke Rogers | 92796a218f | |
Luke Rogers | 45a1f65748 | |
Luke Rogers | 160785e92c | |
Luke Rogers | 763d73ea7a | |
xxyy | 694cbbe81f | |
Luke Rogers | e8876c2c48 | |
Luke Rogers | b0c6815085 | |
Luke Rogers | f7af1b0726 | |
Luke Rogers | 2c6e01a41d | |
Luke Rogers | b6558ccdd5 | |
Luke Rogers | 27b62feb5f | |
Luke Rogers | 29258fdcac | |
Steven Smith | 86a0333afd | |
Luke Rogers | 526d1de989 | |
Luke Rogers | d6683b32f4 | |
Luke Rogers | 004ceb79fc | |
Luke Rogers | a76ff29d4b | |
Luke Rogers | 4447e6cd1f | |
Luke Rogers | b4d7e200a3 | |
Luke Rogers | 7265413a21 | |
Dabo Ross | 6086a7c6da | |
Luke Rogers | 6169be6da1 | |
Luke Rogers | ee0ad67a16 | |
Luke Rogers | ada153c8bc | |
Luke Rogers | 3a13621ae4 | |
Dabo Ross | 289561e76a | |
Luke Rogers | c21b358f9b | |
Luke Rogers | 136acd126d | |
Luke Rogers | a7e931541f | |
Luke Rogers | 223bef30a5 | |
Luke Rogers | cd5ae1d32b | |
Luke Rogers | 6cc7554cd8 | |
Luke Rogers | 398b34fad5 | |
Luke Rogers | 10ef353782 | |
Luke Rogers | 1c9216ac1d | |
Luke Rogers | a9d0d006ab | |
Luke Rogers | c2884242b3 | |
Luke Rogers | 294a97b629 | |
Luke Rogers | 2fde060dc4 | |
Luke Rogers | df1023da55 | |
Dabo Ross | 954ff2ad00 | |
Luke Rogers | afdf5ccfe9 | |
Luke Rogers | fa66cf3c87 | |
Steven Smith | 6c6c108549 | |
Luke Rogers | 90bb260f5f | |
Nathan Blaney | af8b07d20b | |
Luke Rogers | 728ab6f60b | |
Luke Rogers | d3aa2d3edd | |
Luke Rogers | 4f5a8e3ed5 | |
Luke Rogers | d7195dce0e | |
Luke Rogers | efef951501 | |
Luke Rogers | ae1c9d0788 | |
Dabo Ross | 8b6647f521 | |
Dabo Ross | aecf61746f | |
Dabo Ross | c5765cc5f6 | |
Luke Rogers | 75908c6ab6 | |
Nathan Blaney | 0ae8d3d5f9 | |
Luke Rogers | 6f6779dcc5 | |
thenoodle68 | 62a92b104b | |
thenoodle68 | a8bf157ced | |
Matt Anthony | d17234828c | |
Luke Rogers | d10dcbac68 | |
Nathan Blaney | 1febd1362d | |
Matt Anthony | 1f403bcd7a | |
Luke Rogers | 6035f1b1fc | |
Luke Rogers | e29ea1c613 | |
Luke Rogers | b25b8d6cec | |
Luke Rogers | cda99f1852 | |
Luke Rogers | e94c607b3e | |
Luke Rogers | 6decd65a19 | |
Luke Rogers | b65e6e5a75 | |
Luke Rogers | fdbcb967bb | |
Luke Rogers | 8b8623eb9e | |
Luke Rogers | 57e53e8eb7 | |
Luke Rogers | be3df6718f | |
Luke Rogers | 1e22ca4995 | |
Luke Rogers | 4236b7bc29 | |
Luke Rogers | 97e7741434 | |
Luke Rogers | 3df53f95a8 | |
Luke Rogers | 91c827a03b | |
Luke Rogers | fb84e17e34 | |
Luke Rogers | 3e073c8278 | |
Luke Rogers | 8317ba0920 | |
Luke Rogers | bb73e70112 | |
Luke Rogers | 017fd4fc2e | |
Luke Rogers | b3a8703d15 | |
Luke Rogers | a37fc245d0 | |
Dabo Ross | 2eb6112f2f | |
Luke Rogers | a59becca73 | |
Luke Rogers | 77459a0879 | |
Luke Rogers | b489b9d3c7 | |
Luke Rogers | 8b18c33ce5 | |
thenoodle68 | cdf8922f4d | |
Luke Rogers | f55c8f8d4e | |
Luke Rogers | 78912b908f | |
Luke Rogers | fb08870f4a | |
Luke Rogers | 2fa3a5a015 | |
Luke Rogers | 6b4f3ac546 | |
Dabo Ross | 323d045868 | |
Luke Rogers | 4ccfd1e3ca | |
Luke Rogers | 834cbaabd8 | |
Luke Rogers | d3fbf1bc80 | |
Luke Rogers | 3825c94c9d | |
Luke Rogers | 9c7e2074f5 | |
Luke Rogers | 8d7f853426 | |
Luke Rogers | 49bb446c27 | |
Luke Rogers | 44c7bceaf4 | |
Luke Rogers | 0bb1574ebd | |
Luke Rogers | 85d8c4e5cb | |
Luke Rogers | 2d5208cbde | |
Luke Rogers | d78f96b3ec | |
Luke Rogers | 9bc72bc57f | |
Luke Rogers | 8d42933554 | |
Luke Rogers | e75fe73563 | |
Luke Rogers | 29b25e0b1f | |
Luke Rogers | 2b5f5e925d | |
Luke Rogers | f95b607914 | |
Luke Rogers | 181b43df71 | |
Luke Rogers | 1ecdd3a246 | |
Luke Rogers | 13c7e5915b | |
Luke Rogers | 9c94cb01f9 | |
thenoodle68 | 2e66e03c7d | |
Dabo Ross | a5a5fd2dfb | |
Andy Eff | cbe4e3177e | |
Luke Rogers | f51bcf4ecf | |
Luke Rogers | 439ef0ba1c | |
Blake Bengtson | b2c9f46d78 | |
Blake Bengtson | 186f272062 | |
Blake Bengtson | df6ee28462 | |
Dabo Ross | 99fe34a0b1 | |
Luke Rogers | cd4b65de3d | |
Luke Rogers | b6d1620cec | |
Luke Rogers | 0a19fd9347 | |
Luke Rogers | 3d739483de | |
Luke Rogers | 12e2a445d7 | |
RobertClarke | d58a8b5c65 | |
Luke Rogers | 5e7d1e9c92 | |
thenoodle68 | 21eb5760fd | |
Steven Smith | b6ce9b7e50 | |
Luke Rogers | 5258e13de2 | |
Luke Rogers | 12203d2580 | |
Luke Rogers | b5a2497b00 | |
Luke Rogers | 05782766e9 | |
Luke Rogers | 853b4b7db8 | |
Luke Rogers | b05056ff28 | |
Luke Rogers | ce0063f4aa | |
Luke Rogers | 2e20487676 | |
Steven Smith | 436dfc356c | |
Nathan Blaney | a42b8513c6 | |
Nathan Blaney | d83d82978e | |
Luke Rogers | 8a6803a22a | |
Fletcher Boyd | c308b7dcb5 | |
Luke Rogers | c9c3fbf671 | |
Luke Rogers | a2c5964438 | |
Luke Rogers | b6c3a233eb | |
Luke Rogers | cfe9ebd054 | |
Luke Rogers | a6130ab780 | |
Luke Rogers | dd40b71252 | |
Luke Rogers | cd30b053f6 | |
Luke Rogers | 42a72f068e | |
Luke Rogers | b5ae12f50b | |
Luke Rogers | 51c866de6d | |
Luke Rogers | 72babfceb2 | |
Luke Rogers | 47eae62559 | |
Luke Rogers | 46f571382c | |
Luke Rogers | 72dce244b4 | |
Luke Rogers | 2ae2a8575a | |
Luke Rogers | b38c540bf3 | |
Luke Rogers | 52f6260e1c | |
Luke Rogers | 651db7168f | |
Luke Rogers | dd50cb4a36 | |
Luke Rogers | 839871f636 | |
Luke Rogers | af9f024d63 | |
Luke Rogers | d673f8d15c | |
Luke Rogers | f8ad033f8e | |
Luke Rogers | 52b14b367a | |
Luke Rogers | a16b1776f8 | |
Luke Rogers | a45d470363 | |
Luke Rogers | 9b92d73f3a | |
Luke Rogers | 0abac29523 | |
Luke Rogers | 3a142e4810 | |
Luke Rogers | 4f2c690b2d | |
Luke Rogers | a49bcc881c | |
Luke Rogers | bfd600cf49 | |
Luke Rogers | f9f15f3597 | |
Steven Smith | aea91cc33f | |
Luke Rogers | 7474904e97 | |
Luke Rogers | a5d5845faf | |
Fletcher Boyd | 92e61a5348 | |
Fletcher Boyd | 57d9ec985a | |
Luke Rogers | afa81ae7c4 | |
Luke Rogers | 80d976ba23 | |
Steven Smith | 764a74a3ea | |
Steven Smith | 5867cb05c7 | |
Steven Smith | f5e4b67777 | |
Luke Rogers | d3e5099dab | |
Luke Rogers | dabf61a520 | |
Luke Rogers | c0b3285c4a | |
Luke Rogers | f98774032f | |
Luke Rogers | 45330ddfb4 | |
Luke Rogers | 348de62f17 | |
Luke Rogers | 5b1258f544 | |
Luke Rogers | 483226d2ad | |
Fletcher Boyd | 0030669682 | |
Luke Rogers | d5163a846a | |
Luke Rogers | 00f7e35a24 | |
Luke Rogers | e5038156ea | |
Steven Smith | a69c4eceb3 | |
Steven Smith | b460fb2fd2 | |
Steven Smith | e8947a603b | |
Steven Smith | ae258e6c37 | |
Luke Rogers | 59d615aa88 | |
Luke Rogers | 8b629ee886 | |
Steven Smith | 1bba6262bc | |
Luke Rogers | d9f613f6f4 | |
Luke Rogers | cb7f9b736f | |
CafogenMod | ecab6076ea | |
Luke Rogers | cbcf1dc4b5 | |
Luke Rogers | 2be0c46b89 | |
Luke Rogers | 15e7825125 | |
Luke Rogers | 8e545b3a31 | |
Luke Rogers | ea39a6fbb9 | |
Luke Rogers | 9a296d4076 | |
Luke Rogers | 2d06a1fc4a | |
Fletcher Boyd | 1b3d3b51e7 | |
Luke Rogers | 5c09ff6967 | |
Luke Rogers | dde9b97223 | |
Luke Rogers | 931eb1f51a | |
Luke Rogers | 10d235623c | |
Luke Rogers | 45ac28b6a6 | |
Luke Rogers | a714354d69 | |
Luke Rogers | 4160015e61 | |
Luke Rogers | bf49b374eb | |
Luke Rogers | 273ca05858 | |
Luke Rogers | 0655f6d0bb | |
Luke Rogers | 178ff6f4e1 | |
Luke Rogers | 92eca3844e | |
Luke Rogers | 27bd43b5ec | |
Luke Rogers | 79f06b034e | |
Luke Rogers | 83cb28283b | |
Luke Rogers | a8255723ba | |
Luke Rogers | 52f8fc97d0 | |
Luke Rogers | 06e78b7871 | |
Luke Rogers | b42aac00d5 | |
Luke Rogers | 285c61fe11 | |
Luke Rogers | a9c084a14d | |
Luke Rogers | 2ef0579fe6 | |
Luke Rogers | a157a250be | |
Luke Rogers | 415740a399 | |
Luke Rogers | 1f699c8f00 | |
Steven Smith | 4cd74ae3fd | |
Luke Rogers | fe220ffc93 | |
Steven Smith | 803ca5da9a | |
Steven Smith | 00368f59b0 | |
Luke Rogers | d3d36891e7 | |
Luke Rogers | e644a0a0ca | |
Fletcher Boyd | efee5072ad | |
Fletcher Boyd | 83faace7f7 | |
Luke Rogers | 143cd8dc96 | |
Luke Rogers | 909306cfbe | |
Luke Rogers | 8ebf1e24ee | |
Luke Rogers | 63cf1f0514 | |
Luke Rogers | b40b4640d8 | |
Fletcher Boyd | 5efca90faa | |
Fletcher Boyd | 1363e27475 | |
Luke Rogers | fac18416a8 | |
Fletcher Boyd | f8e6ec1e9c | |
Fletcher Boyd | c2d021a07a | |
Luke Rogers | 54d6453aa4 | |
Fletcher Boyd | 893f621dd0 | |
Fletcher Boyd | d29b3c313f | |
Fletcher Boyd | 75d4af1393 | |
Fletcher Boyd | 5f87bf0e76 | |
Fletcher Boyd | 2fb0f811b8 | |
Fletcher Boyd | 07b5abff7b | |
Fletcher Boyd | 14530f07f1 | |
Fletcher Boyd | 2103c64dd5 | |
Fletcher Boyd | d9324eaf95 | |
Luke Rogers | 382b76ca26 | |
Fletcher Boyd | d39e101f45 | |
Fletcher Boyd | 363d0ba6d9 | |
Fletcher Boyd | c08d3c2daf | |
Luke Rogers | 847f1e468d | |
Steven Smith | 3d4bd6a694 | |
Luke Rogers | 8a83eb9b5b | |
Fletcher Boyd | 20e96e1e07 | |
Luke Rogers | 1610c14c6f | |
Luke Rogers | bc2612e9ae | |
Luke Rogers | 29ac513c2b | |
Luke Rogers | 74fc62139c | |
Luke Rogers | f96a975b70 | |
Fletcher Boyd | 8f789222b1 | |
Fletcher Boyd | f12066df0a | |
Luke Rogers | 49e306568d | |
Luke Rogers | b6e9b175db | |
Luke Rogers | 033dd8af12 | |
Nathan Blaney | 114975d058 | |
Nathan Blaney | 6b3b4fc562 | |
Luke Rogers | af2382aea5 | |
Luke Rogers | 1dbf309971 | |
Luke Rogers | 9efedde734 | |
Luke Rogers | 8e121f5b5f | |
Luke Rogers | 8881f694fa | |
Luke Rogers | f788af8d55 | |
Fletcher Boyd | 00e3fb2dc1 | |
Luke Rogers | d7ea20cd3b | |
Luke Rogers | 5844d914de | |
Luke Rogers | 80dd3ad573 | |
Fletcher Boyd | 5a586bb373 | |
Fletcher Boyd | 1ce777a5e4 | |
Fletcher Boyd | 015dd93df3 | |
Fletcher Boyd | 0aa2185ede | |
Fletcher Boyd | 7ab4f756fe | |
Luke Rogers | 6c4d7db976 | |
Luke Rogers | 06546ffdd9 | |
Fletcher Boyd | 2812569593 | |
Fletcher Boyd | a211115b7e | |
Fletcher Boyd | 8c60a923ba | |
Luke Rogers | 304a2bed00 | |
Fletcher Boyd | bc1062e8d6 | |
Luke Rogers | a92209119d | |
Fletcher Boyd | 5a8b2c2825 | |
Fletcher Boyd | b3c08a33da | |
Fletcher Boyd | 73aef2e9ae | |
Luke Rogers | 6247409906 | |
Luke Rogers | 8b23ba9c12 | |
Luke Rogers | 4460254f28 | |
Fletcher Boyd | 9eeebe1a9d | |
Luke Rogers | e061427207 | |
Luke Rogers | 0f488665aa | |
Luke Rogers | 059024cb61 | |
Luke Rogers | a44e0bb7bf | |
Fletcher Boyd | 4b4541dd99 | |
Fletcher Boyd | d5a5237fb8 | |
Fletcher Boyd | 253ffaafda | |
Fletcher Boyd | 9d6aef731e | |
Fletcher Boyd | d58646628c | |
Luke Rogers | fabda30543 | |
Fletcher Boyd | df9fe0e8ac | |
Fletcher Boyd | bc2362e6a6 | |
Fletcher Boyd | 33c6ee6f19 | |
Fletcher Boyd | f54f0edff8 | |
Fletcher Boyd | 706ddbfbe9 | |
Luke Rogers | 270785a31a | |
Luke Rogers | 1a3094242b | |
Fletcher Boyd | d6bdf3ab38 | |
Fletcher Boyd | 397842f518 | |
thenoodle68 | caf45fbae2 | |
Luke Rogers | cc0231e849 | |
Luke Rogers | 99b14bac59 | |
Fletcher Boyd | 7c6453be1e | |
Luke Rogers | f85bc3e935 | |
Fletcher Boyd | 2457f6da00 | |
Luke Rogers | 242ba0d18d | |
Fletcher Boyd | b8d9610e56 | |
Fletcher Boyd | fa97d4659c | |
Fletcher Boyd | fb62640ccc | |
Fletcher Boyd | b08bc570d1 | |
Fletcher Boyd | 4069dd21a3 | |
Fletcher Boyd | 146ae3c279 | |
Luke Rogers | 3c0ba0bdd2 | |
Luke Rogers | 5e161486ff | |
Luke Rogers | 13d284a861 | |
Luke Rogers | 8769b97dc0 | |
Luke Rogers | 488d00e758 | |
Luke Rogers | 543a1cd4d7 | |
Luke Rogers | 6de14f33e3 | |
Luke Rogers | 94b6026b3f | |
Luke Rogers | 6a325b5333 | |
Steven Smith | bc0eb32d9b | |
Luke Rogers | 9fadc40d97 | |
Steven Smith | 4fb4e3c1fb | |
Steven Smith | 4129d2d889 | |
Luke Rogers | 4e0a0b9d6d | |
Daniel Barnes | 0e6e744605 | |
Luke Rogers | 2089a53c50 | |
Luke Rogers | b499c3848e | |
Luke Rogers | 201589e7ec | |
Luke Rogers | 2793fe4895 | |
Luke Rogers | aee1974264 | |
Steven Smith | 28c449bde5 | |
Steven Smith | 81d7ce08c3 | |
Steven Smith | 5d89f4d598 | |
Steven Smith | 3573bac4a9 | |
Daniel Barnes | 81b37c09ed | |
Eric | 4647e767da | |
Steven Smith | 4bf05b36c9 | |
Luke Rogers | 3a6f18f67b | |
Cybo Jenix | 27ac526438 | |
Luke Rogers | 7e8b53d17a | |
Steven Smith | b2b5270686 | |
Steven Smith | 86be633213 | |
Steven Smith | 53d49a78f1 | |
Steven Smith | 15b2b259df | |
Steven Smith | 9356e4e9ca | |
Luke Rogers | 32593e3be8 | |
CafogenMod | 9c9ce943a9 | |
Luke Rogers | aded5c0aff | |
Luke Rogers | cb664117d5 | |
Luke Rogers | c05b6b25c2 | |
Steven Smith | ee8e2cb5e7 | |
Steven Smith | 88beb69f1a | |
Luke Rogers | 08b1e82f41 | |
Steven Smith | 23e75115d2 | |
Luke Rogers | 48b52ac872 | |
Luke Rogers | 0f77d94da7 | |
Steven Smith | 7818049e23 | |
Luke Rogers | ea2f1ba633 | |
Steven Smith | a049b13927 | |
Luke Rogers | 8c01183cfc | |
Luke Rogers | 807a2b43fd | |
Luke Rogers | af4bcdb621 | |
Cybo Jenix | 05b90ea82c | |
Luke Rogers | 88e6e642df | |
cybojenix | 17c29feb60 | |
cybojenix | a8349814a0 | |
Luke Rogers | 1018481499 | |
cybojenix | 3204d15bde | |
cybojenix | d3429f3297 | |
Luke Rogers | ce4a786680 | |
Luke Rogers | 8b304637eb | |
Nathan Blaney | 52e170020e | |
Luke Rogers | 5d94ed4387 | |
Luke Rogers | 350991ec1f | |
Luke Rogers | 38a9eda09a | |
Cybo Jenix | 9e8bbbdcbf | |
Luke Rogers | c88d8ff1b6 | |
Luke Rogers | 1d74e455a2 | |
Luke Rogers | 5d0754f21c | |
Luke Rogers | eb845d1062 | |
cybojenix | a3844480a1 | |
Luke Rogers | 132ad104ed | |
Luke Rogers | 66fd6ca1d5 | |
Nathan Blaney | 41fe6723fc | |
Nathan Blaney | a02761bbd4 | |
Luke Rogers | e5fb97f128 | |
Nathan Blaney | aa80de0bb4 | |
Steven Smith | fd9689e5ce | |
Nathan Blaney | 0160d86a64 | |
Luke Rogers | 4d74493610 | |
Luke Rogers | e74a92e252 | |
Sepero | 6bd692f34a | |
Luke Rogers | ba6c2ae2c8 | |
Luke Rogers | adaab18192 | |
Luke Rogers | 644813c0d8 | |
Nathan Blaney | 383fe71b4d | |
Luke Rogers | 8a9638fcf3 | |
Steven Smith | 9c95dd6590 | |
Luke Rogers | 4a04feff11 | |
Steven Smith | 50c2825ea7 | |
Luke Rogers | 58a857380d | |
Luke Rogers | fb2b5fc26e | |
Luke Rogers | e574a0d41e | |
Luke Rogers | e281bbd62b | |
Luke Rogers | e6af470a16 | |
Luke Rogers | 1feb6d1a1b | |
Luke Rogers | 8460d8d3a0 | |
Steven Smith | cf1732d917 | |
Steven Smith | 65a1827004 | |
Luke Rogers | 205294dea7 | |
Luke Rogers | 77499cbb3e | |
Mike Leigh | 2a3c0e1723 | |
Luke Rogers | 4c273705b9 | |
Luke Rogers | 272ba283e9 | |
Luke Rogers | 52a1e58db1 | |
Luke Rogers | e764890636 | |
Luke Rogers | 2c0879f972 | |
Luke Rogers | 150e6acda7 | |
Luke Rogers | 5e99e1a82f | |
Luke Rogers | ec09da2d93 | |
Steven Smith | e025103d39 | |
Luke Rogers | 810802ec65 | |
Steven Smith | 4868ecf976 | |
Luke Rogers | 63cc79371d | |
Luke Rogers | 94244c7c86 | |
Luke Rogers | 1a6efcdb4b | |
Luke Rogers | 98395457e5 | |
Luke Rogers | afff36b190 | |
Luke Rogers | 20c33f02d5 | |
Luke Rogers | 7f57f3c8ca | |
Luke Rogers | 0bdbade75f | |
Luke Rogers | e6d669be2b | |
Luke Rogers | 28dd4cf5f7 | |
Luke Rogers | 9becfa9897 | |
Luke Rogers | baa98ee5cc | |
Luke Rogers | d5ddc72a53 | |
Luke Rogers | d69feb68d8 | |
Luke Rogers | 78173b7f63 | |
Luke Rogers | 412c03f2ed | |
Luke Rogers | f0842cb887 | |
Luke Rogers | c0e8c4efdc | |
Luke Rogers | c6ecd1db81 | |
Luke Rogers | d26de98ebe | |
Steven Smith | e042aa1bbe | |
Luke Rogers | 0100f1d071 | |
Luke Rogers | ba21f11f6b | |
Luke Rogers | 0ee036df56 | |
Luke Rogers | 4a38289022 | |
Steven Smith | 7963e05e93 | |
Steven Smith | b208635d1b | |
Luke Rogers | 7ad1ad20b2 | |
Luke Rogers | 9467014bf9 | |
Luke Rogers | 585f4f2c4a | |
Luke Rogers | 715358298d | |
Luke Rogers | 0f217fa3dd | |
Luke Rogers | 67f111b6a6 | |
Steven Smith | 29f1ace599 | |
Luke Rogers | 19821b35c8 | |
Luke Rogers | cac785f957 | |
Luke Rogers | 510f70decb | |
Luke Rogers | 67a6554d2f | |
Steven Smith | 4d298dc898 | |
Luke Rogers | 6fdcd83491 | |
Steven Smith | 0d35422d87 | |
Steven Smith | 963f512d1e | |
Luke Rogers | 21d608763b | |
Luke Rogers | 07db016e42 | |
Luke Rogers | fd4a8ac6fa | |
Steven Smith | 6750b076d8 | |
Cybo Jenix | 85f4cd724f | |
Steven Smith | c59aa4470a | |
Steven Smith | 5a8aa32e24 | |
Luke Rogers | a8b830af4f | |
Steven Smith | ea3462558e | |
Steven Smith | ff5769d773 | |
Steven Smith | 3d78028b43 | |
Luke Rogers | bf69674661 | |
Luke Rogers | 04cf2fcdf0 | |
Luke Rogers | 8873d61c0a | |
Luke Rogers | a3669e6b50 | |
Steven Smith | 6ea9187a3a | |
Luke Rogers | 85ec22a005 | |
Luke Rogers | dea3b1b896 | |
Steven Smith | f0c8669dff | |
Luke Rogers | 3fe26a91b1 | |
Luke Rogers | 4324d538af | |
Luke Rogers | 24c1e946c3 | |
Luke Rogers | f230439c56 | |
Luke Rogers | b0e43c0aea | |
Luke Rogers | 487ee03e6c | |
Luke Rogers | 2182d5a0fd | |
Luke Rogers | 263e791138 | |
Luke Rogers | 5d30398bc1 | |
Luke Rogers | 6564338b94 | |
Luke Rogers | 98f9e814b7 | |
Luke Rogers | bc6d604897 | |
Luke Rogers | 90df679324 | |
Luke Rogers | 5033332985 | |
Luke Rogers | 08fae5e286 | |
Jonas Friedmann | 6595f7d54a | |
Luke Rogers | 48cbc834de | |
Luke Rogers | 27e05421fa | |
Luke Rogers | 986567a97f | |
Luke Rogers | ffa63ead4a | |
Luke Rogers | 75b5e2bc6d | |
Luke Rogers | 9779943b91 | |
Luke Rogers | eba21e1584 | |
Luke Rogers | 1be43d9e51 | |
Luke Rogers | fa630eb285 | |
Luke Rogers | 18deba34f3 | |
Luke Rogers | 6b76ba3e2a | |
Luke Rogers | 696f2b7c1a | |
Luke Rogers | e091a48744 | |
Luke Rogers | b98b6ad933 | |
Luke Rogers | 536cfcb87c | |
Steven Smith | a9bd3c4c68 | |
Steven Smith | 190953f866 | |
Steven Smith | af6c37ee2e | |
Steven Smith | 7dbd59e1f2 | |
Steven Smith | 10f1f06e55 | |
Luke Rogers | ef1bf8338c | |
Steven Smith | 4df5510281 | |
Steven Smith | 569d84a75f | |
Luke Rogers | 15d19dd1d8 | |
Luke Rogers | 7159ad8a0e | |
Steven Smith | ba9d015ac2 | |
Steven Smith | cc5c81544d | |
Luke Rogers | 79a25f2600 | |
Luke Rogers | 1817609c9c | |
Steven Smith | 4ef52d3762 | |
Luke Rogers | 9f5fc95d5b | |
Luke Rogers | ebc3720f1e | |
blha303 | 7603bb7b6a | |
Luke Rogers | 88c179f568 | |
Robert "Red" English | 617eb42f17 | |
Luke Rogers | 8c363e5f76 | |
nasonfish | b6d2139e6e | |
Luke Rogers | 5b30095b3d | |
blha303 | 81ccdd2849 | |
Luke Rogers | c4e5e91c14 | |
Luke Rogers | 3d74b7781d | |
Luke Rogers | 16ad0cfbf6 | |
Luke Rogers | c6edec3000 | |
KsaRedFx | 2885273f6d | |
KsaRedFx | 0f689f4583 | |
KsaRedFx | cf7658d28e | |
Robert "Red" English | 50195ba8cd | |
Steven Smith | f980e6ed9f | |
Luke Rogers | fc2e8e56a4 | |
Luke Rogers | f46f752878 | |
Neer Sighted | d517130082 | |
Robert "Red" English | 29ec904777 | |
Robert "Red" English | 57dc9bbaf6 | |
Robert "Red" English | 6bc35605e5 | |
Robert "Red" English | 2a33c97f4d | |
Neer Sighted | 501f3c8af8 | |
blha303 | 8ac4450ff7 | |
Steven Smith | dce3c35424 | |
blha303 | 5a64ce4983 | |
Luke Rogers | 6516caccdc | |
blha303 | 7737c1a373 | |
blha303 | 8f4bc45c56 | |
Steven Smith | 18340b3f3c | |
Neer Sighted | 8278e659ef | |
Edža | 8c83bd3c50 | |
Luke Rogers | 072e73f3a1 | |
Luke Rogers | 08e3cd3c13 | |
Luke Rogers | 3885dd0ac9 | |
Luke Rogers | 93fe9610f4 | |
Luke Rogers | ceb09f4334 |
|
@ -0,0 +1,18 @@
|
|||
# CloudBot editor configuration normalization
|
||||
# Copied from Drupal (GPL)
|
||||
# @see http://editorconfig.org/
|
||||
|
||||
# This is the top-most .editorconfig file; do not search in parent directories.
|
||||
root = true
|
||||
|
||||
# All files.
|
||||
[*]
|
||||
end_of_line = LF
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Not in the spec yet:
|
||||
# @see https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
|
@ -1,5 +1,6 @@
|
|||
persist
|
||||
config
|
||||
config.ssl
|
||||
gitflow
|
||||
*.db
|
||||
*.log
|
||||
|
@ -7,7 +8,8 @@ gitflow
|
|||
*.pyc
|
||||
*.orig
|
||||
.project
|
||||
.pydevproject
|
||||
.geany
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
*.sublime-workspace
|
||||
.idea/
|
||||
plugins/data/GeoLiteCity.dat
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
# How to contribute
|
||||
|
||||
I like to encourage you to contribute to the repository.
|
||||
This should be as easy as possible for you but there are a few things to consider when contributing.
|
||||
The following guidelines for contribution should be followed if you want to submit a pull request.
|
||||
|
||||
## TL;DR
|
||||
|
||||
* Read [Github documentation](http://help.github.com/) and [Pull Request documentation](http://help.github.com/send-pull-requests/)
|
||||
* Fork the repository
|
||||
* Edit the files, add new files
|
||||
* Check the files with [`pep8`](https://pypi.python.org/pypi/pep8), fix any reported errors
|
||||
* Check that the files work as expected in CloudBot
|
||||
* Create a new branch with a descriptive name for your feature (optional)
|
||||
* Commit changes, push to your fork on GitHub
|
||||
* Create a new pull request, provide a short summary of changes in the title line, with more information in the description field.
|
||||
* After submitting the pull request, join the IRC channel (irc.esper.net #cloudbot) and paste a link to the pull request so people are aware of it
|
||||
* After discussion, your pull request will be accepted or rejected.
|
||||
|
||||
## How to prepare
|
||||
|
||||
* You need a [GitHub account](https://github.com/signup/free)
|
||||
* Submit an [issue ticket](https://github.com/ClouDev/CloudBot/issues) for your issue if the is no one yet.
|
||||
* Describe the issue and include steps to reproduce if it's a bug.
|
||||
* Ensure to mention the earliest version that you know is affected.
|
||||
* If you are able and want to fix this, fork the repository on GitHub
|
||||
|
||||
## Make Changes
|
||||
|
||||
* In your forked repository, create a topic branch for your upcoming patch. (e.g. `feature--autoplay` or `bugfix--ios-crash`)
|
||||
* Usually this is based on the develop branch.
|
||||
* Create a branch based on master; `git branch
|
||||
fix/develop/my_contribution develop` then checkout the new branch with `git
|
||||
checkout fix/develop/my_contribution`. Please avoid working directly on the `develop` branch.
|
||||
* Make sure you stick to the coding style that is used already.
|
||||
* Make use of the [`.editorconfig`](http://editorconfig.org/) file.
|
||||
* Make commits of logical units and describe them properly.
|
||||
* Check for unnecessary whitespace with `git diff --check` before committing.
|
||||
* Check your changes with [`pep8`](https://pypi.python.org/pypi/pep8). Ignore messages about line length.
|
||||
|
||||
## Submit Changes
|
||||
|
||||
* Push your changes to a topic branch in your fork of the repository.
|
||||
* Open a pull request to the original repository and choose the right original branch you want to patch.
|
||||
_Advanced users may use [`hub`](https://github.com/defunkt/hub#git-pull-request) gem for that._
|
||||
* If not done in commit messages (which you really should do) please reference and update your issue with the code changes. But _please do not close the issue yourself_.
|
||||
_Notice: You can [turn your previously filed issues into a pull-request here](http://issue2pr.herokuapp.com/)._
|
||||
* Even if you have write access to the repository, do not directly push or merge pull-requests. Let another team member review your pull request and approve.
|
||||
|
||||
# Additional Resources
|
||||
|
||||
* [General GitHub documentation](http://help.github.com/)
|
||||
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
|
||||
* [Read the Issue Guidelines by @necolas](https://github.com/necolas/issue-guidelines/blob/master/CONTRIBUTING.md) for more details
|
||||
* [This CONTRIBUTING.md from here](https://github.com/anselmh/CONTRIBUTING.md)
|
|
@ -0,0 +1,34 @@
|
|||
Thanks to everyone who has contributed to CloudBot! Come in IRC and ping me if I forgot anyone.
|
||||
|
||||
Luke Rogers (lukeroge)
|
||||
Neersighted
|
||||
blha303
|
||||
cybojenix
|
||||
KsaRedFx
|
||||
nathanblaney
|
||||
thenoodle68
|
||||
nasonfish
|
||||
urbels
|
||||
puffrfish
|
||||
Sepero
|
||||
TheFiZi
|
||||
mikeleigh
|
||||
Spudstabber
|
||||
frozenMC
|
||||
frdmn
|
||||
|
||||
|
||||
|
||||
We are using code from the following projects:
|
||||
./plugins/mlia.py - https://github.com/infinitylabs/UguuBot
|
||||
./plugins/horoscope.py - https://github.com/infinitylabs/UguuBot
|
||||
color section in ./plugins/utility.py - https://github.com/hitzler/homero
|
||||
|
||||
Special Thanks:
|
||||
Rmmh (created skybot!)
|
||||
lahwran (for his advice and stuff I stole from his skybot fork!)
|
||||
TheNoodle (for helping with some plugins when I was first starting out)
|
||||
|
||||
If any of your code is in here and you don't have credit, I'm sorry. I didn't keep track of a lot of code I added in the early days of the project.
|
||||
|
||||
You are all awesome :)
|
|
@ -1 +0,0 @@
|
|||
Please see the wiki @ http://git.io/cloudbotircwiki
|
|
@ -1,25 +1,12 @@
|
|||
# CloudBot/DEV
|
||||
# CloudBot
|
||||
|
||||
## About
|
||||
|
||||
CloudBot is a Python IRC bot very heavily based on [Skybot](http://git.io/skybot) by [rmmh](http://git.io/rmmh).
|
||||
|
||||
### Goals
|
||||
|
||||
* Easy to use wrapper
|
||||
* Intuitive configuration
|
||||
* Fully controlled from IRC
|
||||
* Fully compatable with existing skybot plugins
|
||||
* Easily extendable
|
||||
* Thorough documentation
|
||||
* Cross-platform
|
||||
* Muti-threaded, efficient
|
||||
* Automatic reloading
|
||||
* Little boilerplate
|
||||
CloudBot is a Python IRC bot based on [Skybot](http://git.io/skybot) by [rmmh](http://git.io/rmmh).
|
||||
|
||||
## Getting and using CloudBot
|
||||
|
||||
### Download
|
||||
### Download
|
||||
|
||||
Get CloudBot at [https://github.com/ClouDev/CloudBot/zipball/develop](https://github.com/ClouDev/CloudBot/zipball/develop "Get CloudBot from Github!").
|
||||
|
||||
|
@ -27,51 +14,33 @@ Unzip the resulting file, and continue to read this document.
|
|||
|
||||
### Install
|
||||
|
||||
Before you can run the bot, you need to install a few Python dependencies. These can be installed with `pip` (The Python package manager):
|
||||
Before you can run the bot, you need to install a few Python dependencies. LXML is required while Enchant and PyDNS are needed for several plugins.
|
||||
|
||||
|
||||
These can be installed with `pip` (The Python package manager):
|
||||
|
||||
[sudo] pip install -r requirements.txt
|
||||
|
||||
If you use `pip`, you will also need the following packages on linux or `pip` will fail to install the requirements.
|
||||
```python, python-dev, libenchant-dev, libenchant1c2a, libxslt-dev, libxml2-dev.```
|
||||
|
||||
#### How to install `pip`
|
||||
|
||||
curl -O http://python-distribute.org/distribute_setup.py # or download with your browser on windows
|
||||
python distribute_setup.py
|
||||
easy_install pip
|
||||
|
||||
If you are unable to use pip, there are Windows installers for LXML available for [64 bit](https://pypi.python.org/packages/2.7/l/lxml/lxml-2.3.win-amd64-py2.7.exe) and [32 bit](https://pypi.python.org/packages/2.7/l/lxml/lxml-2.3.win32-py2.7.exe) versions of Python.
|
||||
|
||||
### Run
|
||||
|
||||
Once you have installed the required dependencies, there are two ways you can run the bot:
|
||||
Before you run the bot, rename `config.default` to `config` and edit it with your preferred settings.
|
||||
|
||||
#### Launcher
|
||||
|
||||
**Note:** Due to some issues with the launcher we recommend you run the bot manually as detailed below.
|
||||
|
||||
The launcher will start the bot as a background process, and allow the bot to close and restart itself. This is only supported on unix-like machines (not Windows).
|
||||
|
||||
For the launcher to work properly, install `screen`, or `daemon` (daemon is recommended):
|
||||
|
||||
`apt-get install screen`
|
||||
|
||||
`apt-get install daemon`
|
||||
|
||||
Once you have installed either `screen` or `daemon`, run the start command:
|
||||
|
||||
`./cloudbot start`
|
||||
|
||||
It will generate a default config for you. Once you have edited the config, run it again with the same command:
|
||||
|
||||
`./cloudbot start`
|
||||
|
||||
This will start up your bot as a background process. To stop it, use `./cloudbot stop`. (Config docs at the [wiki](http://git.io/cloudbotircconfig))
|
||||
|
||||
#### Manually
|
||||
|
||||
To manually run the bot and get console output, run it with:
|
||||
Once you have installed the required dependencies and renamed the config file, you can run the bot! Make sure you are in the correct folder and run the following command:
|
||||
|
||||
`python bot.py`
|
||||
|
||||
On Windows you can usually just double-click the `bot.py` file to start the bot, as long as you have Python installed correctly.
|
||||
|
||||
(note: running the bot without the launcher breaks the start and restart commands)
|
||||
On Windows you can usually just double-click `bot.py` to start the bot, as long as you have Python installed correctly.
|
||||
|
||||
## Getting help with CloudBot
|
||||
|
||||
|
@ -83,6 +52,8 @@ To write your own plugins, visit the [Plugin Wiki Page](http://git.io/cloudbotir
|
|||
|
||||
More at the [Wiki Main Page](http://git.io/cloudbotircwiki).
|
||||
|
||||
(some of the information on the wiki is outdated and needs to be rewritten)
|
||||
|
||||
### Support
|
||||
|
||||
The developers reside in [#CloudBot](irc://irc.esper.net/cloudbot) on [EsperNet](http://esper.net) and would be glad to help you.
|
||||
|
@ -91,31 +62,25 @@ If you think you have found a bug/have a idea/suggestion, please **open a issue*
|
|||
|
||||
### Requirements
|
||||
|
||||
CloudBot runs on **Python** *2.7.x*. It is developed on **Ubuntu** *12.04* with **Python** *2.7.3*.
|
||||
CloudBot runs on **Python** *2.7.x*. It is currently developed on **Windows** *8* with **Python** *2.7.5*.
|
||||
|
||||
It **requires the Python module** `lXML`, and `Enchant` is needed for the spellcheck plugin.
|
||||
It **requires the Python module** lXML.
|
||||
The module `Enchant` is needed for the spellcheck plugin.
|
||||
The module `PyDNS` is needed for SRV record lookup in the mcping plugin.
|
||||
|
||||
The programs `daemon` or `screen` are recomended for the launcher to run optimaly.
|
||||
|
||||
**Windows** users: Windows compatibility with the launcher and some plugins is **broken** (such as ping), but we do intend to add it.³
|
||||
**Windows** users: Windows compatibility some plugins is **broken** (such as ping), but we do intend to add it. Eventually.
|
||||
|
||||
## Example CloudBots
|
||||
|
||||
The developers of CloudBot run two CloudBots on [Espernet](http://esper.net).
|
||||
|
||||
They can both be found in [#CloudBot](irc://irc.esper.net/cloudbot "Connect via IRC to #CloudBot on irc.esper.net").
|
||||
|
||||
**mau5bot** is the semi-stable bot, and runs on the latest stable development version of CloudBot. (mau5bot is running on **Ubuntu Server** *12.04* with **Python** *2.7.3*)
|
||||
|
||||
**neerbot** is unstable bot, and runs on the `HEAD` of the `develop` branch. (neerbot is running on **Debian** *Wheezy/Testing* with **Python** *2.7.2*)
|
||||
You can find a number of example bots in [#CloudBot](irc://irc.esper.net/cloudbot "Connect via IRC to #CloudBot on irc.esper.net").
|
||||
|
||||
## License
|
||||
|
||||
CloudBot is **licensed** under the **GPL v3** license. The terms are as follows.
|
||||
|
||||
CloudBot/DEV
|
||||
CloudBot
|
||||
|
||||
Copyright © 2011-2012 Luke Rogers / ClouDev - <[cloudev.github.com](http://cloudev.github.com)>
|
||||
Copyright © 2011-2013 Luke Rogers
|
||||
|
||||
CloudBot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -129,7 +94,3 @@ CloudBot is **licensed** under the **GPL v3** license. The terms are as follows.
|
|||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CloudBot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
## Notes
|
||||
|
||||
³ eventually
|
||||
|
|
|
@ -1,49 +1,32 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
__author__ = "ClouDev"
|
||||
__authors__ = ["Lukeroge", "neersighted"]
|
||||
__copyright__ = "Copyright 2012, ClouDev"
|
||||
__credits__ = ["thenoodle", "_frozen", "rmmh"]
|
||||
__license__ = "GPL v3"
|
||||
__version__ = "DEV"
|
||||
__maintainer__ = "ClouDev"
|
||||
__email__ = "cloudev@neersighted.com"
|
||||
__status__ = "Development"
|
||||
|
||||
import os
|
||||
import Queue
|
||||
import sys
|
||||
import time
|
||||
import platform
|
||||
import re
|
||||
|
||||
sys.path += ['plugins'] # so 'import hook' works without duplication
|
||||
sys.path += ['lib']
|
||||
sys.path += ['plugins', 'lib'] # add stuff to the sys.path for easy imports
|
||||
os.chdir(sys.path[0] or '.') # do stuff relative to the install directory
|
||||
|
||||
|
||||
class Bot(object):
|
||||
pass
|
||||
|
||||
print 'CloudBot %s (%s) <http://git.io/cloudbotirc>' % (__version__, __status__)
|
||||
|
||||
# print debug info
|
||||
opsys = platform.platform()
|
||||
python_imp = platform.python_implementation()
|
||||
python_ver = platform.python_version()
|
||||
architecture = ' '.join(platform.architecture())
|
||||
|
||||
print "Operating System: %s, Python " \
|
||||
"Version: %s %s, Architecture: %s" \
|
||||
"" % (opsys, python_imp, python_ver, architecture)
|
||||
print 'CloudBot DEV <http://git.io/cloudbotirc>'
|
||||
|
||||
# create new bot object
|
||||
bot = Bot()
|
||||
bot.vars = {}
|
||||
|
||||
# record start time for the uptime command
|
||||
bot.start_time = time.time()
|
||||
|
||||
print 'Loading plugins...'
|
||||
print 'Begin Plugin Loading.'
|
||||
|
||||
# bootstrap the reloader
|
||||
eval(compile(open(os.path.join('core', 'reload.py'), 'U').read(),
|
||||
os.path.join('core', 'reload.py'), 'exec'))
|
||||
os.path.join('core', 'reload.py'), 'exec'))
|
||||
reload(init=True)
|
||||
|
||||
config()
|
||||
|
@ -56,14 +39,17 @@ bot.conns = {}
|
|||
|
||||
try:
|
||||
for name, conf in bot.config['connections'].iteritems():
|
||||
# strip all spaces and capitalization from the connection name
|
||||
name = name.replace(" ", "_")
|
||||
name = re.sub('[^A-Za-z0-9_]+', '', name)
|
||||
print 'Connecting to server: %s' % conf['server']
|
||||
if conf.get('ssl'):
|
||||
bot.conns[name] = SSLIRC(name, conf['server'], conf['nick'], conf=conf,
|
||||
port=conf.get('port', 6667), channels=conf['channels'],
|
||||
ignore_certificate_errors=conf.get('ignore_cert', True))
|
||||
port=conf.get('port', 6667), channels=conf['channels'],
|
||||
ignore_certificate_errors=conf.get('ignore_cert', True))
|
||||
else:
|
||||
bot.conns[name] = IRC(name, conf['server'], conf['nick'], conf=conf,
|
||||
port=conf.get('port', 6667), channels=conf['channels'])
|
||||
port=conf.get('port', 6667), channels=conf['channels'])
|
||||
except Exception as e:
|
||||
print 'ERROR: malformed config file', e
|
||||
sys.exit()
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"connections": {
|
||||
"hackint": {
|
||||
"server": "irc.hackint.eu",
|
||||
"nick": "antibot",
|
||||
"user": "antibot",
|
||||
"realname": "CloudBot - http://git.io/cloudbotirc",
|
||||
"mode": "",
|
||||
"_nickserv_password": "",
|
||||
"-nickserv_user": "",
|
||||
"channels": [
|
||||
"#ChaosChemnitz",
|
||||
"#logbot"
|
||||
],
|
||||
"invite_join": true,
|
||||
"auto_rejoin": false,
|
||||
"command_prefix": "."
|
||||
}
|
||||
},
|
||||
"disabled_plugins": [],
|
||||
"disabled_commands": [],
|
||||
"acls": {},
|
||||
"api_keys": {
|
||||
"tvdb": "",
|
||||
"wolframalpha": "",
|
||||
"lastfm": "",
|
||||
"rottentomatoes": "",
|
||||
"soundcloud": "",
|
||||
"twitter_consumer_key": "",
|
||||
"twitter_consumer_secret": "",
|
||||
"twitter_access_token": "",
|
||||
"twitter_access_secret": "",
|
||||
"wunderground": "",
|
||||
"googletranslate": "",
|
||||
"rdio_key": "",
|
||||
"rdio_secret": ""
|
||||
},
|
||||
"permissions": {
|
||||
"admins": {
|
||||
"perms": [
|
||||
"adminonly",
|
||||
"addfactoid",
|
||||
"delfactoid",
|
||||
"ignore",
|
||||
"botcontrol",
|
||||
"permissions_users",
|
||||
"op"
|
||||
],
|
||||
"users": [
|
||||
"examplea!user@example.com",
|
||||
"exampleb!user@example.com"
|
||||
]
|
||||
},
|
||||
"moderators": {
|
||||
"perms": [
|
||||
"addfactoid",
|
||||
"delfactoid",
|
||||
"ignore"
|
||||
],
|
||||
"users": [
|
||||
"stummi!~Stummi@stummi.org"
|
||||
]
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"factoids": {
|
||||
"prefix": false
|
||||
},
|
||||
"ignore": {
|
||||
"ignored": []
|
||||
}
|
||||
},
|
||||
"censored_strings": [
|
||||
"mypass",
|
||||
"mysecret"
|
||||
]
|
||||
}
|
|
@ -7,60 +7,8 @@ def save(conf):
|
|||
json.dump(conf, open('config', 'w'), sort_keys=True, indent=2)
|
||||
|
||||
if not os.path.exists('config'):
|
||||
open('config', 'w').write(inspect.cleandoc(
|
||||
r'''
|
||||
{
|
||||
"connections":
|
||||
{
|
||||
"EsperNet":
|
||||
{
|
||||
"server": "irc.esper.net",
|
||||
"nick": "MyNewCloudBot",
|
||||
"user": "cloudbot",
|
||||
"realname": "CloudBot - http://git.io/cloudbotirc",
|
||||
"nickserv_password": "",
|
||||
"channels": ["#cloudbot"],
|
||||
"invite_join": true,
|
||||
"auto_rejoin": false,
|
||||
"command_prefix": "."
|
||||
}
|
||||
},
|
||||
"disabled_plugins": [],
|
||||
"disabled_commands": [],
|
||||
"acls": {},
|
||||
"api_keys":
|
||||
{
|
||||
"geoip": "INSERT API KEY FROM ipinfodb.com HERE",
|
||||
"tvdb": "INSERT API KEY FROM thetvdb.com HERE",
|
||||
"bitly_user": "INSERT USERNAME FROM bitly.com HERE",
|
||||
"bitly_api": "INSERT API KEY FROM bitly.com HERE",
|
||||
"wolframalpha": "INSERT API KEY FROM wolframalpha.com HERE",
|
||||
"lastfm": "INSERT API KEY FROM lastfm HERE",
|
||||
"rottentomatoes": "INSERT API KEY FROM rottentomatoes HERE",
|
||||
"mc_user": "INSERT minecraft USERNAME HERE",
|
||||
"mc_pass": "INSERT minecraft PASSWORD HERE"
|
||||
},
|
||||
"plugins":
|
||||
{
|
||||
"factoids":
|
||||
{
|
||||
"prefix": false
|
||||
},
|
||||
"ignore":
|
||||
{
|
||||
"ignored": []
|
||||
}
|
||||
},
|
||||
"censored_strings":
|
||||
[
|
||||
"mypass",
|
||||
"mysecret"
|
||||
],
|
||||
"admins": ["myname@myhost"]
|
||||
}''') + '\n')
|
||||
print "Config generated!"
|
||||
print "Please edit the config now!"
|
||||
print "For help, see http://git.io/cloudbotircwiki"
|
||||
print "Please rename 'config.default' to 'config' to set up your bot!"
|
||||
print "For help, see http://git.io/cloudbotirc"
|
||||
print "Thank you for using CloudBot!"
|
||||
sys.exit()
|
||||
|
||||
|
@ -77,4 +25,3 @@ def config():
|
|||
|
||||
|
||||
bot._config_mtime = 0
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ threaddbs = {}
|
|||
|
||||
|
||||
def get_db_connection(conn, name=''):
|
||||
"returns an sqlite3 connection to a persistent database"
|
||||
"""returns an sqlite3 connection to a persistent database"""
|
||||
|
||||
if not name:
|
||||
name = '{}.db'.format(conn.name)
|
||||
|
|
|
@ -17,16 +17,18 @@ def decode(txt):
|
|||
|
||||
|
||||
def censor(text):
|
||||
text = text.replace('\n', '').replace('\r', '')
|
||||
replacement = '[censored]'
|
||||
if 'censored_strings' in bot.config:
|
||||
words = map(re.escape, bot.config['censored_strings'])
|
||||
regex = re.compile('(%s)' % "|".join(words))
|
||||
text = regex.sub(replacement, text)
|
||||
if bot.config['censored_strings']:
|
||||
words = map(re.escape, bot.config['censored_strings'])
|
||||
regex = re.compile('({})'.format("|".join(words)))
|
||||
text = regex.sub(replacement, text)
|
||||
return text
|
||||
|
||||
|
||||
class crlf_tcp(object):
|
||||
"Handles tcp connections that consist of utf-8 lines ending with crlf"
|
||||
"""Handles tcp connections that consist of utf-8 lines ending with crlf"""
|
||||
|
||||
def __init__(self, host, port, timeout=300):
|
||||
self.ibuffer = ""
|
||||
|
@ -42,7 +44,16 @@ class crlf_tcp(object):
|
|||
return socket.socket(socket.AF_INET, socket.TCP_NODELAY)
|
||||
|
||||
def run(self):
|
||||
self.socket.connect((self.host, self.port))
|
||||
noerror = 0
|
||||
while 1:
|
||||
try:
|
||||
self.socket.connect((self.host, self.port))
|
||||
break
|
||||
except socket.gaierror as e:
|
||||
time.sleep(5)
|
||||
except socket.timeout as e:
|
||||
time.sleep(5)
|
||||
|
||||
thread.start_new_thread(self.recv_loop, ())
|
||||
thread.start_new_thread(self.send_loop, ())
|
||||
|
||||
|
@ -53,17 +64,25 @@ class crlf_tcp(object):
|
|||
return socket.timeout
|
||||
|
||||
def handle_receive_exception(self, error, last_timestamp):
|
||||
print("Receive exception: %s" % (error))
|
||||
if time.time() - last_timestamp > self.timeout:
|
||||
print("Receive timeout. Restart connection.")
|
||||
self.iqueue.put(StopIteration)
|
||||
self.socket.close()
|
||||
return True
|
||||
return False
|
||||
|
||||
def handle_send_exception(self, error):
|
||||
print("Send exception: %s" % (error))
|
||||
self.iqueue.put(StopIteration)
|
||||
self.socket.close()
|
||||
return True
|
||||
|
||||
def recv_loop(self):
|
||||
last_timestamp = time.time()
|
||||
while True:
|
||||
try:
|
||||
data = self.recv_from_socket(4096)
|
||||
data = self.recv_from_socket(4096)
|
||||
self.ibuffer += data
|
||||
if data:
|
||||
last_timestamp = time.time()
|
||||
|
@ -77,6 +96,8 @@ class crlf_tcp(object):
|
|||
if self.handle_receive_exception(e, last_timestamp):
|
||||
return
|
||||
continue
|
||||
except AttributeError:
|
||||
return
|
||||
|
||||
while '\r\n' in self.ibuffer:
|
||||
line, self.ibuffer = self.ibuffer.split('\r\n', 1)
|
||||
|
@ -84,24 +105,31 @@ class crlf_tcp(object):
|
|||
|
||||
def send_loop(self):
|
||||
while True:
|
||||
line = self.oqueue.get().splitlines()[0][:500]
|
||||
print ">>> %r" % line
|
||||
self.obuffer += line.encode('utf-8', 'replace') + '\r\n'
|
||||
while self.obuffer:
|
||||
sent = self.socket.send(self.obuffer)
|
||||
self.obuffer = self.obuffer[sent:]
|
||||
try:
|
||||
line = self.oqueue.get().splitlines()[0][:500]
|
||||
if line == StopIteration:
|
||||
return
|
||||
print ">>> %r" % line
|
||||
self.obuffer += line.encode('utf-8', 'replace') + '\r\n'
|
||||
while self.obuffer:
|
||||
sent = self.socket.send(self.obuffer)
|
||||
self.obuffer = self.obuffer[sent:]
|
||||
|
||||
except socket.error as e:
|
||||
self.handle_send_exception(e)
|
||||
return
|
||||
|
||||
class crlf_ssl_tcp(crlf_tcp):
|
||||
"Handles ssl tcp connetions that consist of utf-8 lines ending with crlf"
|
||||
"""Handles ssl tcp connetions that consist of utf-8 lines ending with crlf"""
|
||||
|
||||
def __init__(self, host, port, ignore_cert_errors, timeout=300):
|
||||
self.ignore_cert_errors = ignore_cert_errors
|
||||
crlf_tcp.__init__(self, host, port, timeout)
|
||||
|
||||
def create_socket(self):
|
||||
return wrap_socket(crlf_tcp.create_socket(self), server_side=False,
|
||||
cert_reqs=CERT_NONE if self.ignore_cert_errors else
|
||||
CERT_REQUIRED)
|
||||
cert_reqs=CERT_NONE if self.ignore_cert_errors else
|
||||
CERT_REQUIRED)
|
||||
|
||||
def recv_from_socket(self, nbytes):
|
||||
return self.socket.read(nbytes)
|
||||
|
@ -111,10 +139,14 @@ class crlf_ssl_tcp(crlf_tcp):
|
|||
|
||||
def handle_receive_exception(self, error, last_timestamp):
|
||||
# this is terrible
|
||||
if not "timed out" in error.args[0]:
|
||||
raise
|
||||
#if not "timed out" in error.args[0]:
|
||||
# raise
|
||||
return crlf_tcp.handle_receive_exception(self, error, last_timestamp)
|
||||
|
||||
def handle_send_exception(self, error):
|
||||
return crlf_tcp.handle_send_exception(self, error)
|
||||
|
||||
|
||||
irc_prefix_rem = re.compile(r'(.*?) (.*?) (.*)').match
|
||||
irc_noprefix_rem = re.compile(r'()(.*?) (.*)').match
|
||||
irc_netmask_rem = re.compile(r':?([^!@]*)!?([^@]*)@?(.*)').match
|
||||
|
@ -122,7 +154,8 @@ irc_param_ref = re.compile(r'(?:^|(?<= ))(:.*|[^ ]+)').findall
|
|||
|
||||
|
||||
class IRC(object):
|
||||
"handles the IRC protocol"
|
||||
"""handles the IRC protocol"""
|
||||
|
||||
def __init__(self, name, server, nick, port=6667, channels=[], conf={}):
|
||||
self.name = name
|
||||
self.channels = channels
|
||||
|
@ -130,6 +163,8 @@ class IRC(object):
|
|||
self.server = server
|
||||
self.port = port
|
||||
self.nick = nick
|
||||
self.history = {}
|
||||
self.vars = {}
|
||||
|
||||
self.out = Queue.Queue() # responses from the server are placed here
|
||||
# format: [rawline, prefix, command, params,
|
||||
|
@ -147,8 +182,8 @@ class IRC(object):
|
|||
self.set_pass(self.conf.get('server_password'))
|
||||
self.set_nick(self.nick)
|
||||
self.cmd("USER",
|
||||
[conf.get('user', 'cloudbot'), "3", "*", conf.get('realname',
|
||||
'CloudBot - http://git.io/cloudbot')])
|
||||
[conf.get('user', 'cloudbot'), "3", "*", conf.get('realname',
|
||||
'CloudBot - http://git.io/cloudbot')])
|
||||
|
||||
def parse_loop(self):
|
||||
while True:
|
||||
|
@ -165,7 +200,7 @@ class IRC(object):
|
|||
else:
|
||||
prefix, command, params = irc_noprefix_rem(msg).groups()
|
||||
nick, user, host = irc_netmask_rem(prefix).groups()
|
||||
mask = user + "@" + host
|
||||
mask = nick + "!" + user + "@" + host
|
||||
paramlist = irc_param_ref(params)
|
||||
lastparam = ""
|
||||
if paramlist:
|
||||
|
@ -174,7 +209,7 @@ class IRC(object):
|
|||
lastparam = paramlist[-1]
|
||||
# put the parsed message in the response queue
|
||||
self.out.put([msg, prefix, command, params, nick, user, host,
|
||||
mask, paramlist, lastparam])
|
||||
mask, paramlist, lastparam])
|
||||
# if the server pings us, pong them back
|
||||
if command == "PING":
|
||||
self.cmd("PONG", paramlist)
|
||||
|
@ -188,7 +223,7 @@ class IRC(object):
|
|||
|
||||
def join(self, channel):
|
||||
""" makes the bot join a channel """
|
||||
self.send("JOIN %s" % channel)
|
||||
self.send("JOIN {}".format(channel))
|
||||
if channel not in self.channels:
|
||||
self.channels.append(channel)
|
||||
|
||||
|
@ -199,13 +234,18 @@ class IRC(object):
|
|||
self.channels.remove(channel)
|
||||
|
||||
def msg(self, target, text):
|
||||
""" makes the bot send a message to a user """
|
||||
""" makes the bot send a PRIVMSG to a target """
|
||||
self.cmd("PRIVMSG", [target, text])
|
||||
|
||||
def ctcp(self, target, ctcp_type, text):
|
||||
""" makes the bot send a PRIVMSG CTCP to a target """
|
||||
out = u"\x01{} {}\x01".format(ctcp_type, text)
|
||||
self.cmd("PRIVMSG", [target, out])
|
||||
|
||||
def cmd(self, command, params=None):
|
||||
if params:
|
||||
params[-1] = ':' + params[-1]
|
||||
self.send(command + ' ' + ' '.join(map(censor, params)))
|
||||
params[-1] = u':' + params[-1]
|
||||
self.send(u"{} {}".format(command, ' '.join(params)))
|
||||
else:
|
||||
self.send(command)
|
||||
|
||||
|
|
|
@ -7,35 +7,40 @@ thread.stack_size(1024 * 512) # reduce vm size
|
|||
|
||||
class Input(dict):
|
||||
def __init__(self, conn, raw, prefix, command, params,
|
||||
nick, user, host, mask, paraml, msg):
|
||||
nick, user, host, mask, paraml, msg):
|
||||
|
||||
chan = paraml[0].lower()
|
||||
if chan == conn.nick.lower(): # is a PM
|
||||
chan = nick
|
||||
|
||||
def say(msg):
|
||||
conn.msg(chan, msg)
|
||||
def message(message, target=chan):
|
||||
"""sends a message to a specific or current channel/user"""
|
||||
conn.msg(target, message)
|
||||
|
||||
def pm(msg):
|
||||
conn.msg(nick, msg)
|
||||
|
||||
def reply(msg):
|
||||
if chan == nick: # PMs don't need prefixes
|
||||
conn.msg(chan, msg)
|
||||
def reply(message, target=chan):
|
||||
"""sends a message to the current channel/user with a prefix"""
|
||||
if target == nick:
|
||||
conn.msg(target, message)
|
||||
else:
|
||||
conn.msg(chan, '(' + nick + ') ' + msg)
|
||||
conn.msg(target, u"({}) {}".format(nick, message))
|
||||
|
||||
def me(msg):
|
||||
conn.msg(chan, "\x01%s %s\x01" % ("ACTION", msg))
|
||||
def action(message, target=chan):
|
||||
"""sends an action to the current channel/user or a specific channel/user"""
|
||||
conn.ctcp(target, "ACTION", message)
|
||||
|
||||
def notice(msg):
|
||||
conn.cmd('NOTICE', [nick, msg])
|
||||
def ctcp(message, ctcp_type, target=chan):
|
||||
"""sends an ctcp to the current channel/user or a specific channel/user"""
|
||||
conn.ctcp(target, ctcp_type, message)
|
||||
|
||||
def notice(message, target=nick):
|
||||
"""sends a notice to the current channel/user or a specific channel/user"""
|
||||
conn.cmd('NOTICE', [target, message])
|
||||
|
||||
dict.__init__(self, conn=conn, raw=raw, prefix=prefix, command=command,
|
||||
params=params, nick=nick, user=user, host=host, mask=mask,
|
||||
paraml=paraml, msg=msg, server=conn.server, chan=chan,
|
||||
notice=notice, say=say, reply=reply, pm=pm, bot=bot,
|
||||
me=me, lastparam=paraml[-1])
|
||||
params=params, nick=nick, user=user, host=host, mask=mask,
|
||||
paraml=paraml, msg=msg, server=conn.server, chan=chan,
|
||||
notice=notice, message=message, reply=reply, bot=bot,
|
||||
action=action, ctcp=ctcp, lastparam=paraml[-1])
|
||||
|
||||
# make dict keys accessible as attributes
|
||||
def __getattr__(self, key):
|
||||
|
@ -77,7 +82,8 @@ def do_sieve(sieve, bot, input, func, type, args):
|
|||
|
||||
|
||||
class Handler(object):
|
||||
'''Runs plugins in their own threads (ensures order)'''
|
||||
"""Runs plugins in their own threads (ensures order)"""
|
||||
|
||||
def __init__(self, func):
|
||||
self.func = func
|
||||
self.input_queue = Queue.Queue()
|
||||
|
@ -103,6 +109,7 @@ class Handler(object):
|
|||
run(self.func, input)
|
||||
except:
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
|
||||
def stop(self):
|
||||
|
@ -115,11 +122,10 @@ class Handler(object):
|
|||
def dispatch(input, kind, func, args, autohelp=False):
|
||||
for sieve, in bot.plugs['sieve']:
|
||||
input = do_sieve(sieve, bot, input, func, kind, args)
|
||||
if input == None:
|
||||
if input is None:
|
||||
return
|
||||
|
||||
if autohelp and args.get('autohelp', True) and not input.inp \
|
||||
and func.__doc__ is not None:
|
||||
if not (not autohelp or not args.get('autohelp', True) or input.inp or not (func.__doc__ is not None)):
|
||||
input.notice(input.conn.conf["command_prefix"] + func.__doc__)
|
||||
return
|
||||
|
||||
|
@ -153,9 +159,9 @@ def main(conn, out):
|
|||
if inp.command == 'PRIVMSG':
|
||||
# COMMANDS
|
||||
if inp.chan == inp.nick: # private message, no command prefix
|
||||
prefix = '^(?:[%s]?|' % command_prefix
|
||||
prefix = '^(?:[{}]?|'.format(command_prefix)
|
||||
else:
|
||||
prefix = '^(?:[%s]|' % command_prefix
|
||||
prefix = '^(?:[{}]|'.format(command_prefix)
|
||||
|
||||
command_re = prefix + inp.conn.nick
|
||||
command_re += r'[,;:]+\s+)(\w+)(?:$|\s+)(.*)'
|
||||
|
@ -168,8 +174,8 @@ def main(conn, out):
|
|||
|
||||
if isinstance(command, list): # multiple potential matches
|
||||
input = Input(conn, *out)
|
||||
input.notice("Did you mean %s or %s?" %
|
||||
(', '.join(command[:-1]), command[-1]))
|
||||
input.notice("Did you mean {} or {}?".format
|
||||
(', '.join(command[:-1]), command[-1]))
|
||||
elif command in bot.commands:
|
||||
input = Input(conn, *out)
|
||||
input.trigger = trigger
|
||||
|
|
|
@ -17,8 +17,8 @@ def make_signature(f):
|
|||
return f.func_code.co_filename, f.func_name, f.func_code.co_firstlineno
|
||||
|
||||
|
||||
def format_plug(plug, kind='', lpad=0, width=40):
|
||||
out = ' ' * lpad + '%s:%s:%s' % make_signature(plug[0])
|
||||
def format_plug(plug, kind='', lpad=0):
|
||||
out = ' ' * lpad + '{}:{}:{}'.format(*make_signature(plug[0]))
|
||||
if kind == 'command':
|
||||
out += ' ' * (50 - len(out)) + plug[1]['name']
|
||||
|
||||
|
@ -49,7 +49,7 @@ def reload(init=False):
|
|||
|
||||
try:
|
||||
eval(compile(open(filename, 'U').read(), filename, 'exec'),
|
||||
globals())
|
||||
globals())
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
if init: # stop if there's an error (syntax?) in a core
|
||||
|
@ -111,20 +111,19 @@ def reload(init=False):
|
|||
|
||||
if not init:
|
||||
print '### new plugin (type: %s) loaded:' % \
|
||||
type, format_plug(data)
|
||||
type, format_plug(data)
|
||||
|
||||
if changed:
|
||||
bot.commands = {}
|
||||
for plug in bot.plugs['command']:
|
||||
name = plug[1]['name'].lower()
|
||||
if not re.match(r'^\w+$', name):
|
||||
print '### ERROR: invalid command name "%s" (%s)' % (name,
|
||||
format_plug(plug))
|
||||
print '### ERROR: invalid command name "{}" ({})'.format(name, format_plug(plug))
|
||||
continue
|
||||
if name in bot.commands:
|
||||
print "### ERROR: command '%s' already registered (%s, %s)" % \
|
||||
(name, format_plug(bot.commands[name]),
|
||||
format_plug(plug))
|
||||
print "### ERROR: command '{}' already registered ({}, {})".format(name,
|
||||
format_plug(bot.commands[name]),
|
||||
format_plug(plug))
|
||||
continue
|
||||
bot.commands[name] = plug
|
||||
|
||||
|
@ -155,7 +154,7 @@ def reload(init=False):
|
|||
for kind, plugs in sorted(bot.plugs.iteritems()):
|
||||
if kind == 'command':
|
||||
continue
|
||||
print ' %s:' % kind
|
||||
print ' {}:'.format(kind)
|
||||
for plug in plugs:
|
||||
print format_plug(plug, kind=kind, lpad=6)
|
||||
print
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
def yaml_load(filename):
|
||||
import yaml
|
||||
fileHandle = open(filename, 'r')
|
||||
stuff = yaml.load(fileHandle.read())
|
||||
fileHandle.close()
|
||||
return stuff
|
||||
|
||||
def yaml_save(stuff, filename):
|
||||
import yaml
|
||||
fileHandle = open (filename, 'w' )
|
||||
fileHandle.write (yaml.dump(stuff))
|
||||
fileHandle.close()
|
||||
|
||||
from util import hook
|
||||
|
||||
@hook.event('*')
|
||||
def tellinput(paraml, input=None, say=None):
|
||||
# import time
|
||||
# now = time.time()
|
||||
# spam = yaml_load('spam')
|
||||
# if spam[input.nick]:
|
||||
# spam[input.nick].append(time.time())
|
||||
# else:
|
||||
# spam[input.nick] = [time.time()]
|
||||
# for x in spam[input.nick]:
|
||||
# if now - x > 5:
|
||||
# spam[input.nick].pop(x)
|
||||
# if len(spam[input.nick]) > 8:
|
||||
# say(":O")
|
||||
# say("HOW COULD YOU "+input.nick)
|
||||
# say("lol!")
|
||||
# yaml_save(spam,'spam')
|
||||
return
|
|
@ -1,33 +0,0 @@
|
|||
import json
|
||||
import random
|
||||
import re
|
||||
|
||||
from util import hook, http
|
||||
|
||||
|
||||
@hook.command
|
||||
def suggest(inp, inp_unstripped=''):
|
||||
".suggest [#n] <phrase> -- gets a random/the nth suggested google search"
|
||||
|
||||
inp = inp_unstripped
|
||||
m = re.match('^#(\d+) (.+)$', inp)
|
||||
if m:
|
||||
num, inp = m.groups()
|
||||
num = int(num)
|
||||
if num > 10:
|
||||
return 'I can only get the first ten suggestions.'
|
||||
else:
|
||||
num = 0
|
||||
|
||||
page = http.get('http://google.com/complete/search', output='json', client='hp', q=inp)
|
||||
page_json = page.split('(', 1)[1][:-1]
|
||||
suggestions = json.loads(page_json)[1]
|
||||
if not suggestions:
|
||||
return 'No suggestions found.'
|
||||
if num:
|
||||
if len(suggestions) + 1 <= num:
|
||||
return 'I only got %d suggestions.' % len(suggestions)
|
||||
out = suggestions[num - 1]
|
||||
else:
|
||||
out = random.choice(suggestions)
|
||||
return '#%d: %s' % (int(out[2][0]) + 1, out[0].replace('<b>', '').replace('</b>', ''))
|
|
@ -0,0 +1,72 @@
|
|||
import random
|
||||
|
||||
from util import hook
|
||||
|
||||
|
||||
with open("plugins/data/larts.txt") as f:
|
||||
larts = [line.strip() for line in f.readlines()
|
||||
if not line.startswith("//")]
|
||||
|
||||
with open("plugins/data/insults.txt") as f:
|
||||
insults = [line.strip() for line in f.readlines()
|
||||
if not line.startswith("//")]
|
||||
|
||||
with open("plugins/data/flirts.txt") as f:
|
||||
flirts = [line.strip() for line in f.readlines()
|
||||
if not line.startswith("//")]
|
||||
|
||||
|
||||
@hook.command
|
||||
def lart(inp, action=None, nick=None, conn=None, notice=None):
|
||||
"""lart <user> -- LARTs <user>."""
|
||||
target = inp.strip()
|
||||
|
||||
if " " in target:
|
||||
notice("Invalid username!")
|
||||
return
|
||||
|
||||
# if the user is trying to make the bot slap itself, slap them
|
||||
if target.lower() == conn.nick.lower() or target.lower() == "itself":
|
||||
target = nick
|
||||
|
||||
values = {"user": target}
|
||||
phrase = random.choice(larts)
|
||||
|
||||
# act out the message
|
||||
action(phrase.format(**values))
|
||||
|
||||
|
||||
@hook.command
|
||||
def insult(inp, nick=None, action=None, conn=None, notice=None):
|
||||
"""insult <user> -- Makes the bot insult <user>."""
|
||||
target = inp.strip()
|
||||
|
||||
if " " in target:
|
||||
notice("Invalid username!")
|
||||
return
|
||||
|
||||
if target == conn.nick.lower() or target == "itself":
|
||||
target = nick
|
||||
else:
|
||||
target = inp
|
||||
|
||||
out = 'insults {}... "{}"'.format(target, random.choice(insults))
|
||||
action(out)
|
||||
|
||||
|
||||
@hook.command
|
||||
def flirt(inp, action=None, conn=None, notice=None):
|
||||
"""flirt <user> -- Make the bot flirt with <user>."""
|
||||
target = inp.strip()
|
||||
|
||||
if " " in target:
|
||||
notice("Invalid username!")
|
||||
return
|
||||
|
||||
if target == conn.nick.lower() or target == "itself":
|
||||
target = 'itself'
|
||||
else:
|
||||
target = inp
|
||||
|
||||
out = 'flirts with {}... "{}"'.format(target, random.choice(flirts))
|
||||
action(out)
|
|
@ -1,5 +1,5 @@
|
|||
'''brainfuck interpreter adapted from (public domain) code at
|
||||
http://brainfuck.sourceforge.net/brain.py'''
|
||||
"""brainfuck interpreter adapted from (public domain) code at
|
||||
http://brainfuck.sourceforge.net/brain.py"""
|
||||
|
||||
import re
|
||||
import random
|
||||
|
@ -14,7 +14,7 @@ MAX_STEPS = 1000000
|
|||
@hook.command('brainfuck')
|
||||
@hook.command
|
||||
def bf(inp):
|
||||
"bf <prog> -- Executes <prog> as Brainfuck code."
|
||||
"""bf <prog> -- Executes <prog> as Brainfuck code."""
|
||||
|
||||
program = re.sub('[^][<>+-.,]', '', inp)
|
||||
|
||||
|
@ -45,10 +45,10 @@ def bf(inp):
|
|||
# the main program loop:
|
||||
while ip < len(program):
|
||||
c = program[ip]
|
||||
if c == '+':
|
||||
memory[mp] = memory[mp] + 1 % 256
|
||||
if c == '+':
|
||||
memory[mp] += 1 % 256
|
||||
elif c == '-':
|
||||
memory[mp] = memory[mp] - 1 % 256
|
||||
memory[mp] -= 1 % 256
|
||||
elif c == '>':
|
||||
mp += 1
|
||||
if mp > rightmost:
|
||||
|
@ -57,7 +57,7 @@ def bf(inp):
|
|||
# no restriction on memory growth!
|
||||
memory.extend([0] * BUFFER_SIZE)
|
||||
elif c == '<':
|
||||
mp = mp - 1 % len(memory)
|
||||
mp -= 1 % len(memory)
|
||||
elif c == '.':
|
||||
output += chr(memory[mp])
|
||||
if len(output) > 500:
|
||||
|
@ -76,7 +76,7 @@ def bf(inp):
|
|||
if steps > MAX_STEPS:
|
||||
if output == '':
|
||||
output = '(no output)'
|
||||
output += '[exceeded %d iterations]' % MAX_STEPS
|
||||
output += '[exceeded {} iterations]'.format(MAX_STEPS)
|
||||
break
|
||||
|
||||
stripped_output = re.sub(r'[\x00-\x1F]', '', output)
|
|
@ -6,8 +6,8 @@ from util import hook
|
|||
|
||||
@hook.command
|
||||
def choose(inp):
|
||||
"choose <choice1>, [choice2], [choice3], [choice4], ... -- " \
|
||||
"Randomly picks one of the given choices."
|
||||
"""choose <choice1>, [choice2], [choice3], [choice4], ... --
|
||||
Randomly picks one of the given choices."""
|
||||
|
||||
c = re.findall(r'([^,]+)', inp)
|
||||
if len(c) == 1:
|
|
@ -0,0 +1,121 @@
|
|||
# from jessi bot
|
||||
import urllib2
|
||||
import hashlib
|
||||
import re
|
||||
import unicodedata
|
||||
from util import hook
|
||||
|
||||
# these are just parts required
|
||||
# TODO: Merge them.
|
||||
|
||||
arglist = ['', 'y', '', '', '', '', '', '', '', '', 'wsf', '',
|
||||
'', '', '', '', '', '', '', '0', 'Say', '1', 'false']
|
||||
|
||||
always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
'abcdefghijklmnopqrstuvwxyz'
|
||||
'0123456789' '_.-')
|
||||
|
||||
headers = {'X-Moz': 'prefetch', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1)Gecko/20100101 Firefox/7.0',
|
||||
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'Referer': 'http://www.cleverbot.com',
|
||||
'Pragma': 'no-cache', 'Cache-Control': 'no-cache, no-cache', 'Accept-Language': 'en-us;q=0.8,en;q=0.5'}
|
||||
|
||||
keylist = ['stimulus', 'start', 'sessionid', 'vText8', 'vText7', 'vText6',
|
||||
'vText5', 'vText4', 'vText3', 'vText2', 'icognoid',
|
||||
'icognocheck', 'prevref', 'emotionaloutput', 'emotionalhistory',
|
||||
'asbotname', 'ttsvoice', 'typing', 'lineref', 'fno', 'sub',
|
||||
'islearning', 'cleanslate']
|
||||
|
||||
MsgList = list()
|
||||
|
||||
|
||||
def quote(s, safe='/'): # quote('abc def') -> 'abc%20def'
|
||||
s = s.encode('utf-8')
|
||||
s = s.decode('utf-8')
|
||||
print "s= " + s
|
||||
print "safe= " + safe
|
||||
safe += always_safe
|
||||
safe_map = dict()
|
||||
for i in range(256):
|
||||
c = chr(i)
|
||||
safe_map[c] = (c in safe) and c or ('%%%02X' % i)
|
||||
try:
|
||||
res = map(safe_map.__getitem__, s)
|
||||
except:
|
||||
print "blank"
|
||||
return ''
|
||||
print "res= " + ''.join(res)
|
||||
return ''.join(res)
|
||||
|
||||
|
||||
def encode(keylist, arglist):
|
||||
text = str()
|
||||
for i in range(len(keylist)):
|
||||
k = keylist[i]
|
||||
v = quote(arglist[i])
|
||||
text += '&' + k + '=' + v
|
||||
text = text[1:]
|
||||
return text
|
||||
|
||||
|
||||
def Send():
|
||||
data = encode(keylist, arglist)
|
||||
digest_txt = data[9:29]
|
||||
new_hash = hashlib.md5(digest_txt).hexdigest()
|
||||
arglist[keylist.index('icognocheck')] = new_hash
|
||||
data = encode(keylist, arglist)
|
||||
req = urllib2.Request('http://www.cleverbot.com/webservicemin',
|
||||
data, headers)
|
||||
f = urllib2.urlopen(req)
|
||||
reply = f.read()
|
||||
return reply
|
||||
|
||||
|
||||
def parseAnswers(text):
|
||||
d = dict()
|
||||
keys = ['text', 'sessionid', 'logurl', 'vText8', 'vText7', 'vText6',
|
||||
'vText5', 'vText4', 'vText3', 'vText2', 'prevref', 'foo',
|
||||
'emotionalhistory', 'ttsLocMP3', 'ttsLocTXT', 'ttsLocTXT3',
|
||||
'ttsText', 'lineRef', 'lineURL', 'linePOST', 'lineChoices',
|
||||
'lineChoicesAbbrev', 'typingData', 'divert']
|
||||
values = text.split('\r')
|
||||
i = 0
|
||||
for key in keys:
|
||||
d[key] = values[i]
|
||||
i += 1
|
||||
return d
|
||||
|
||||
|
||||
def ask(inp):
|
||||
arglist[keylist.index('stimulus')] = inp
|
||||
if MsgList:
|
||||
arglist[keylist.index('lineref')] = '!0' + str(len(
|
||||
MsgList) / 2)
|
||||
asw = Send()
|
||||
MsgList.append(inp)
|
||||
answer = parseAnswers(asw)
|
||||
for k, v in answer.iteritems():
|
||||
try:
|
||||
arglist[keylist.index(k)] = v
|
||||
except ValueError:
|
||||
pass
|
||||
arglist[keylist.index('emotionaloutput')] = str()
|
||||
text = answer['ttsText']
|
||||
MsgList.append(text)
|
||||
return text
|
||||
|
||||
|
||||
@hook.command("cb")
|
||||
def cleverbot(inp, reply=None):
|
||||
reply(ask(inp))
|
||||
|
||||
|
||||
''' # TODO: add in command to control extra verbose per channel
|
||||
@hook.event('PRIVMSG')
|
||||
def cbevent(inp, reply=None):
|
||||
reply(ask(inp))
|
||||
|
||||
@hook.command("cbver", permissions=['cleverbot'])
|
||||
def cleverbotverbose(inp, notice=None):
|
||||
if on in input
|
||||
'''
|
|
@ -1,10 +1,11 @@
|
|||
from util import hook
|
||||
import random
|
||||
|
||||
from util import hook
|
||||
|
||||
|
||||
@hook.command(autohelp=False)
|
||||
def coin(inp, me=None):
|
||||
"coin [amount] -- Flips [amount] of coins."
|
||||
def coin(inp, action=None):
|
||||
"""coin [amount] -- Flips [amount] of coins."""
|
||||
|
||||
if inp:
|
||||
try:
|
||||
|
@ -15,11 +16,10 @@ def coin(inp, me=None):
|
|||
amount = 1
|
||||
|
||||
if amount == 1:
|
||||
me("flips a coin and gets %s." % random.choice(["heads", "tails"]))
|
||||
action("flips a coin and gets {}.".format(random.choice(["heads", "tails"])))
|
||||
elif amount == 0:
|
||||
me("makes a coin flipping motion with its hands.")
|
||||
action("makes a coin flipping motion with its hands.")
|
||||
else:
|
||||
heads = int(random.normalvariate(.5 * amount, (.75 * amount) ** .5))
|
||||
tails = amount - heads
|
||||
me("flips %i coins and gets " \
|
||||
"%i heads and %i tails." % (amount, heads, tails))
|
||||
action("flips {} coins and gets {} heads and {} tails.".format(amount, heads, tails))
|
|
@ -0,0 +1,37 @@
|
|||
from util import hook
|
||||
|
||||
import re
|
||||
|
||||
CORRECTION_RE = r'^(s|S)/.*/.*/?\S*$'
|
||||
|
||||
|
||||
@hook.regex(CORRECTION_RE)
|
||||
def correction(match, input=None, conn=None, message=None):
|
||||
split = input.msg.split("/")
|
||||
|
||||
if len(split) == 4:
|
||||
nick = split[3].lower()
|
||||
else:
|
||||
nick = None
|
||||
|
||||
find = split[1]
|
||||
replace = split[2]
|
||||
|
||||
for item in conn.history[input.chan].__reversed__():
|
||||
name, timestamp, msg = item
|
||||
if msg.startswith("s/"):
|
||||
# don't correct corrections, it gets really confusing
|
||||
continue
|
||||
if nick:
|
||||
if nick != name.lower():
|
||||
continue
|
||||
if find in msg:
|
||||
if "\x01ACTION" in msg:
|
||||
msg = msg.replace("\x01ACTION ", "/me ").replace("\x01", "")
|
||||
message(u"Correction, <{}> {}".format(name, msg.replace(find, "\x02" + replace + "\x02")))
|
||||
return
|
||||
else:
|
||||
continue
|
||||
|
||||
return u"Did not find {} in any recent messages.".format(find)
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
from util import http, hook
|
||||
|
||||
## CONSTANTS
|
||||
|
||||
exchanges = {
|
||||
"blockchain": {
|
||||
"api_url": "https://blockchain.info/ticker",
|
||||
"func": lambda data: u"Blockchain // Buy: \x0307${:,.2f}\x0f -"
|
||||
u" Sell: \x0307${:,.2f}\x0f".format(data["USD"]["buy"], data["USD"]["sell"])
|
||||
},
|
||||
"coinbase": {
|
||||
"api_url": "https://coinbase.com/api/v1/prices/spot_rate",
|
||||
"func": lambda data: u"Coinbase // Current: \x0307${:,.2f}\x0f".format(float(data['amount']))
|
||||
},
|
||||
"bitpay": {
|
||||
"api_url": "https://bitpay.com/api/rates",
|
||||
"func": lambda data: u"Bitpay // Current: \x0307${:,.2f}\x0f".format(data[0]['rate'])
|
||||
},
|
||||
"bitstamp": {
|
||||
"api_url": "https://www.bitstamp.net/api/ticker/",
|
||||
"func": lambda data: u"BitStamp // Current: \x0307${:,.2f}\x0f - High: \x0307${:,.2f}\x0f -"
|
||||
u" Low: \x0307${:,.2f}\x0f - Volume: {:,.2f} BTC".format(float(data['last']),
|
||||
float(data['high']),
|
||||
float(data['low']),
|
||||
float(data['volume']))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
## HOOK FUNCTIONS
|
||||
|
||||
@hook.command("btc", autohelp=False)
|
||||
@hook.command(autohelp=False)
|
||||
def bitcoin(inp):
|
||||
"""bitcoin <exchange> -- Gets current exchange rate for bitcoins from several exchanges, default is Blockchain.
|
||||
Supports MtGox, Bitpay, Coinbase and BitStamp."""
|
||||
inp = inp.lower()
|
||||
|
||||
if inp:
|
||||
if inp in exchanges:
|
||||
exchange = exchanges[inp]
|
||||
else:
|
||||
return "Invalid Exchange"
|
||||
else:
|
||||
exchange = exchanges["blockchain"]
|
||||
|
||||
data = http.get_json(exchange["api_url"])
|
||||
func = exchange["func"]
|
||||
return func(data)
|
||||
|
||||
|
||||
@hook.command("ltc", autohelp=False)
|
||||
@hook.command(autohelp=False)
|
||||
def litecoin(inp, message=None):
|
||||
"""litecoin -- gets current exchange rate for litecoins from BTC-E"""
|
||||
data = http.get_json("https://btc-e.com/api/2/ltc_usd/ticker")
|
||||
ticker = data['ticker']
|
||||
message("Current: \x0307${:,.2f}\x0f - High: \x0307${:,.2f}\x0f"
|
||||
" - Low: \x0307${:,.2f}\x0f - Volume: {:,.2f} LTC".format(ticker['buy'], ticker['high'], ticker['low'],
|
||||
ticker['vol_cur']))
|
|
@ -0,0 +1,39 @@
|
|||
import base64
|
||||
|
||||
from util import hook
|
||||
|
||||
|
||||
def encode(key, clear):
|
||||
enc = []
|
||||
for i in range(len(clear)):
|
||||
key_c = key[i % len(key)]
|
||||
enc_c = chr((ord(clear[i]) + ord(key_c)) % 256)
|
||||
enc.append(enc_c)
|
||||
return base64.urlsafe_b64encode("".join(enc))
|
||||
|
||||
|
||||
def decode(key, enc):
|
||||
dec = []
|
||||
enc = base64.urlsafe_b64decode(enc.encode('ascii', 'ignore'))
|
||||
for i in range(len(enc)):
|
||||
key_c = key[i % len(key)]
|
||||
dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256)
|
||||
dec.append(dec_c)
|
||||
return "".join(dec)
|
||||
|
||||
|
||||
@hook.command
|
||||
def cypher(inp):
|
||||
"""cypher <pass> <string> -- Cyphers <string> with <password>."""
|
||||
|
||||
passwd = inp.split(" ")[0]
|
||||
inp = " ".join(inp.split(" ")[1:])
|
||||
return encode(passwd, inp)
|
||||
|
||||
|
||||
@hook.command
|
||||
def decypher(inp):
|
||||
"""decypher <pass> <string> -- Decyphers <string> with <password>."""
|
||||
passwd = inp.split(" ")[0]
|
||||
inp = " ".join(inp.split(" ")[1:])
|
||||
return decode(passwd, inp)
|
Binary file not shown.
|
@ -44,4 +44,11 @@ Do you live on a chicken farm? Because you sure know how to raise cocks.
|
|||
Are you wearing space pants? Because your ass is out of this world.
|
||||
Nice legs. What time do they open?
|
||||
Are you lost? Because it’s so strange to see an angel so far from heaven.
|
||||
Your daddy must have been a baker, because you've got a nice set of buns.
|
||||
Your daddy must have been a baker, because you've got a nice set of buns.
|
||||
You're so beautiful that last night you made me forget my pickup line.
|
||||
I've never seen such dark eyes with so much light in them.
|
||||
I think we should just be friends with sexual tension.
|
||||
Whenever I see you I feel like a dog dying to get out of the car.
|
||||
If I'd have held you any closer I'd be in back of you.
|
||||
I wish I were on Facebook so I could poke you.
|
||||
I want you like JFK wanted a car with a roof.
|
|
@ -0,0 +1,620 @@
|
|||
1 Stone
|
||||
1:1 Granite
|
||||
1:2 Polished Granite
|
||||
1:3 Diorite
|
||||
1:4 Polished Diorite
|
||||
1:5 Andesite
|
||||
1:6 Polished Andesite
|
||||
2 Grass
|
||||
3 Dirt
|
||||
3:1 Dirt (No Grass)
|
||||
3:2 Podzol
|
||||
4 Cobblestone
|
||||
5 Wooden Plank (Oak)
|
||||
5:1 Wooden Plank (Spruce)
|
||||
5:2 Wooden Plank (Birch)
|
||||
5:3 Wooden Plank (Jungle)
|
||||
5:4 Wooden Plank (Acacia)
|
||||
5:5 Wooden Plank (Dark Oak)
|
||||
6 Sapling (Oak)
|
||||
6:1 Sapling (Spruce)
|
||||
6:2 Sapling (Birch)
|
||||
6:3 Sapling (Jungle)
|
||||
6:4 Sapling (Acacia)
|
||||
6:5 Sapling (Dark Oak)
|
||||
7 Bedrock
|
||||
8 Water
|
||||
9 Water (No Spread)
|
||||
10 Lava
|
||||
11 Lava (No Spread)
|
||||
12 Sand
|
||||
12:1 Red Sand
|
||||
13 Gravel
|
||||
14 Gold Ore
|
||||
15 Iron Ore
|
||||
16 Coal Ore
|
||||
17 Wood (Oak)
|
||||
17:1 Wood (Spruce)
|
||||
17:2 Wood (Birch)
|
||||
17:3 Wood (Jungle)
|
||||
17:4 Wood (Oak 4)
|
||||
17:5 Wood (Oak 5)
|
||||
18 Leaves (Oak)
|
||||
18:1 Leaves (Spruce)
|
||||
18:2 Leaves (Birch)
|
||||
18:3 Leaves (Jungle)
|
||||
19 Sponge
|
||||
20 Glass
|
||||
21 Lapis Lazuli Ore
|
||||
22 Lapis Lazuli Block
|
||||
23 Dispenser
|
||||
24 Sandstone
|
||||
24:1 Sandstone (Chiseled)
|
||||
24:2 Sandstone (Smooth)
|
||||
25 Note Block
|
||||
26 Bed (Block)
|
||||
27 Rail (Powered)
|
||||
28 Rail (Detector)
|
||||
29 Sticky Piston
|
||||
30 Cobweb
|
||||
31 Tall Grass (Dead Shrub)
|
||||
31:1 Tall Grass
|
||||
31:2 Tall Grass (Fern)
|
||||
32 Dead Shrub
|
||||
33 Piston
|
||||
34 Piston (Head)
|
||||
35 Wool
|
||||
35:1 Orange Wool
|
||||
35:2 Magenta Wool
|
||||
35:3 Light Blue Wool
|
||||
35:4 Yellow Wool
|
||||
35:5 Lime Wool
|
||||
35:6 Pink Wool
|
||||
35:7 Gray Wool
|
||||
35:8 Light Gray Wool
|
||||
35:9 Cyan Wool
|
||||
35:10 Purple Wool
|
||||
35:11 Blue Wool
|
||||
35:12 Brown Wool
|
||||
35:13 Green Wool
|
||||
35:14 Red Wool
|
||||
35:15 Black Wool
|
||||
36 Piston (Moving)
|
||||
37 Dandelion
|
||||
38 Poppy
|
||||
38:1 Blue Orchid
|
||||
38:2 Allium
|
||||
38:4 Red Tulip
|
||||
38:5 Orange Tulip
|
||||
38:6 White Tulip
|
||||
38:7 Pink Tulip
|
||||
38:8 Oxeye Daisy
|
||||
39 Brown Mushroom
|
||||
40 Red Mushroom
|
||||
41 Block of Gold
|
||||
42 Block of Iron
|
||||
43 Stone Slab (Double)
|
||||
43:1 Sandstone Slab (Double)
|
||||
43:2 Wooden Slab (Double)
|
||||
43:3 Cobblestone Slab (Double)
|
||||
43:4 Brick Slab (Double)
|
||||
43:5 Stone Brick Slab (Double)
|
||||
43:6 Nether Brick Slab (Double)
|
||||
43:7 Quartz Slab (Double)
|
||||
43:8 Smooth Stone Slab (Double)
|
||||
43:9 Smooth Sandstone Slab (Double)
|
||||
44 Stone Slab
|
||||
44:1 Sandstone Slab
|
||||
44:2 Wooden Slab
|
||||
44:3 Cobblestone Slab
|
||||
44:4 Brick Slab
|
||||
44:5 Stone Brick Slab
|
||||
44:6 Nether Brick Slab
|
||||
44:7 Quartz Slab
|
||||
45 Brick
|
||||
46 TNT
|
||||
47 Bookshelf
|
||||
48 Moss Stone
|
||||
49 Obsidian
|
||||
50 Torch
|
||||
51 Fire
|
||||
52 Mob Spawner
|
||||
53 Wooden Stairs (Oak)
|
||||
54 Chest
|
||||
55 Redstone Wire
|
||||
56 Diamond Ore
|
||||
57 Block of Diamond
|
||||
58 Workbench
|
||||
59 Wheat (Crop)
|
||||
60 Farmland
|
||||
61 Furnace
|
||||
62 Furnace (Smelting)
|
||||
63 Sign (Block)
|
||||
64 Wood Door (Block)
|
||||
65 Ladder
|
||||
66 Rail
|
||||
67 Cobblestone Stairs
|
||||
68 Sign (Wall Block)
|
||||
69 Lever
|
||||
70 Stone Pressure Plate
|
||||
71 Iron Door (Block)
|
||||
72 Wooden Pressure Plate
|
||||
73 Redstone Ore
|
||||
74 Redstone Ore (Glowing)
|
||||
75 Redstone Torch (Off)
|
||||
76 Redstone Torch
|
||||
77 Button (Stone)
|
||||
78 Snow
|
||||
79 Ice
|
||||
80 Snow Block
|
||||
81 Cactus
|
||||
82 Clay Block
|
||||
83 Sugar Cane (Block)
|
||||
84 Jukebox
|
||||
85 Fence
|
||||
86 Pumpkin
|
||||
87 Netherrack
|
||||
88 Soul Sand
|
||||
89 Glowstone
|
||||
90 Portal
|
||||
91 Jack-O-Lantern
|
||||
92 Cake (Block)
|
||||
93 Redstone Repeater (Block Off)
|
||||
94 Redstone Repeater (Block On)
|
||||
95 Stained Glass (White)
|
||||
95:1 Stained Glass (Orange)
|
||||
95:2 Stained Glass (Magenta)
|
||||
95:3 Stained Glass (Light Blue)
|
||||
95:4 Stained Glass (Yellow)
|
||||
95:5 Stained Glass (Lime)
|
||||
95:6 Stained Glass (Pink)
|
||||
95:7 Stained Glass (Gray)
|
||||
95:8 Stained Glass (Light Grey)
|
||||
95:9 Stained Glass (Cyan)
|
||||
95:10 Stained Glass (Purple)
|
||||
95:11 Stained Glass (Blue)
|
||||
95:12 Stained Glass (Brown)
|
||||
95:13 Stained Glass (Green)
|
||||
95:14 Stained Glass (Red)
|
||||
95:15 Stained Glass (Black)
|
||||
96 Trapdoor
|
||||
97 Monster Egg (Stone)
|
||||
97:1 Monster Egg (Cobblestone)
|
||||
97:2 Monster Egg (Stone Brick)
|
||||
97:3 Monster Egg (Mossy Stone Brick)
|
||||
97:4 Monster Egg (Cracked Stone)
|
||||
97:5 Monster Egg (Chiseled Stone)
|
||||
98 Stone Bricks
|
||||
98:1 Mossy Stone Bricks
|
||||
98:2 Cracked Stone Bricks
|
||||
98:3 Chiseled Stone Brick
|
||||
99 Brown Mushroom (Block)
|
||||
100 Red Mushroom (Block)
|
||||
101 Iron Bars
|
||||
102 Glass Pane
|
||||
103 Melon (Block)
|
||||
104 Pumpkin Vine
|
||||
105 Melon Vine
|
||||
106 Vines
|
||||
107 Fence Gate
|
||||
108 Brick Stairs
|
||||
109 Stone Brick Stairs
|
||||
110 Mycelium
|
||||
111 Lily Pad
|
||||
112 Nether Brick
|
||||
113 Nether Brick Fence
|
||||
114 Nether Brick Stairs
|
||||
115 Nether Wart
|
||||
116 Enchantment Table
|
||||
117 Brewing Stand (Block)
|
||||
118 Cauldron (Block)
|
||||
119 End Portal
|
||||
120 End Portal Frame
|
||||
121 End Stone
|
||||
122 Dragon Egg
|
||||
123 Redstone Lamp
|
||||
124 Redstone Lamp (On)
|
||||
125 Oak-Wood Slab (Double)
|
||||
125:1 Spruce-Wood Slab (Double)
|
||||
125:2 Birch-Wood Slab (Double)
|
||||
125:3 Jungle-Wood Slab (Double)
|
||||
125:4 Acacia Wood Slab (Double)
|
||||
125:5 Dark Oak Wood Slab (Double)
|
||||
126 Oak-Wood Slab
|
||||
126:1 Spruce-Wood Slab
|
||||
126:2 Birch-Wood Slab
|
||||
126:3 Jungle-Wood Slab
|
||||
126:4 Acacia Wood Slab
|
||||
126:5 Dark Oak Wood Slab
|
||||
127 Cocoa Plant
|
||||
128 Sandstone Stairs
|
||||
129 Emerald Ore
|
||||
130 Ender Chest
|
||||
131 Tripwire Hook
|
||||
132 Tripwire
|
||||
133 Block of Emerald
|
||||
134 Wooden Stairs (Spruce)
|
||||
135 Wooden Stairs (Birch)
|
||||
136 Wooden Stairs (Jungle)
|
||||
137 Command Block
|
||||
138 Beacon
|
||||
139 Cobblestone Wall
|
||||
139:1 Mossy Cobblestone Wall
|
||||
140 Flower Pot (Block)
|
||||
141 Carrot (Crop)
|
||||
142 Potatoes (Crop)
|
||||
143 Button (Wood)
|
||||
144 Head Block (Skeleton)
|
||||
144:1 Head Block (Wither)
|
||||
144:2 Head Block (Zombie)
|
||||
144:3 Head Block (Steve)
|
||||
144:4 Head Block (Creeper)
|
||||
145 Anvil
|
||||
145:1 Anvil (Slightly Damaged)
|
||||
145:2 Anvil (Very Damaged)
|
||||
146 Trapped Chest
|
||||
147 Weighted Pressure Plate (Light)
|
||||
148 Weighted Pressure Plate (Heavy)
|
||||
149 Redstone Comparator (Off)
|
||||
150 Redstone Comparator (On)
|
||||
151 Daylight Sensor
|
||||
152 Block of Redstone
|
||||
153 Nether Quartz Ore
|
||||
154 Hopper
|
||||
155 Quartz Block
|
||||
155:1 Chiseled Quartz Block
|
||||
155:2 Pillar Quartz Block
|
||||
156 Quartz Stairs
|
||||
157 Rail (Activator)
|
||||
158 Dropper
|
||||
159 Stained Clay (White)
|
||||
159:1 Stained Clay (Orange)
|
||||
159:2 Stained Clay (Magenta)
|
||||
159:3 Stained Clay (Light Blue)
|
||||
159:4 Stained Clay (Yellow)
|
||||
159:5 Stained Clay (Lime)
|
||||
159:6 Stained Clay (Pink)
|
||||
159:7 Stained Clay (Gray)
|
||||
159:8 Stained Clay (Light Gray)
|
||||
159:9 Stained Clay (Cyan)
|
||||
159:10 Stained Clay (Purple)
|
||||
159:11 Stained Clay (Blue)
|
||||
159:12 Stained Clay (Brown)
|
||||
159:13 Stained Clay (Green)
|
||||
159:14 Stained Clay (Red)
|
||||
159:15 Stained Clay (Black)
|
||||
160 Stained Glass Pane (White)
|
||||
160:1 Stained Glass Pane (Orange)
|
||||
160:2 Stained Glass Pane (Magenta)
|
||||
160:3 Stained Glass Pane (Light Blue)
|
||||
160:4 Stained Glass Pane (Yellow)
|
||||
160:5 Stained Glass Pane (Lime)
|
||||
160:6 Stained Glass Pane (Pink)
|
||||
160:7 Stained Glass Pane (Gray)
|
||||
160:8 Stained Glass Pane (Light Gray)
|
||||
160:9 Stained Glass Pane (Cyan)
|
||||
160:10 Stained Glass Pane (Purple)
|
||||
160:11 Stained Glass Pane (Blue)
|
||||
160:12 Stained Glass Pane (Brown)
|
||||
160:13 Stained Glass Pane (Green)
|
||||
160:14 Stained Glass Pane (Red)
|
||||
160:15 Stained Glass Pane (Black)
|
||||
162 Wood (Acacia Oak)
|
||||
162:1 Wood (Dark Oak)
|
||||
163 Wooden Stairs (Acacia)
|
||||
164 Wooden Stairs (Dark Oak)
|
||||
165 Slime Block
|
||||
170 Hay Bale
|
||||
171 Carpet (White)
|
||||
171:1 Carpet (Orange)
|
||||
171:2 Carpet (Magenta)
|
||||
171:3 Carpet (Light Blue)
|
||||
171:4 Carpet (Yellow)
|
||||
171:5 Carpet (Lime)
|
||||
171:6 Carpet (Pink)
|
||||
171:7 Carpet (Grey)
|
||||
171:8 Carpet (Light Gray)
|
||||
171:9 Carpet (Cyan)
|
||||
171:10 Carpet (Purple)
|
||||
171:11 Carpet (Blue)
|
||||
171:12 Carpet (Brown)
|
||||
171:13 Carpet (Green)
|
||||
171:14 Carpet (Red)
|
||||
171:15 Carpet (Black)
|
||||
172 Hardened Clay
|
||||
173 Block of Coal
|
||||
174 Packed Ice
|
||||
175 Sunflower
|
||||
175:1 Lilac
|
||||
175:2 Double Tallgrass
|
||||
175:3 Large Fern
|
||||
175:4 Rose Bush
|
||||
175:5 Peony
|
||||
256 Iron Shovel
|
||||
257 Iron Pickaxe
|
||||
258 Iron Axe
|
||||
259 Flint and Steel
|
||||
260 Apple
|
||||
261 Bow
|
||||
262 Arrow
|
||||
263 Coal
|
||||
263:1 Charcoal
|
||||
264 Diamond Gem
|
||||
265 Iron Ingot
|
||||
266 Gold Ingot
|
||||
267 Iron Sword
|
||||
268 Wooden Sword
|
||||
269 Wooden Shovel
|
||||
270 Wooden Pickaxe
|
||||
271 Wooden Axe
|
||||
272 Stone Sword
|
||||
273 Stone Shovel
|
||||
274 Stone Pickaxe
|
||||
275 Stone Axe
|
||||
276 Diamond Sword
|
||||
277 Diamond Shovel
|
||||
278 Diamond Pickaxe
|
||||
279 Diamond Axe
|
||||
280 Stick
|
||||
281 Bowl
|
||||
282 Mushroom Stew
|
||||
283 Gold Sword
|
||||
284 Gold Shovel
|
||||
285 Gold Pickaxe
|
||||
286 Gold Axe
|
||||
287 String
|
||||
288 Feather
|
||||
289 Gunpowder
|
||||
290 Wooden Hoe
|
||||
291 Stone Hoe
|
||||
292 Iron Hoe
|
||||
293 Diamond Hoe
|
||||
294 Gold Hoe
|
||||
295 Wheat Seeds
|
||||
296 Wheat
|
||||
297 Bread
|
||||
298 Leather Helmet
|
||||
299 Leather Chestplate
|
||||
300 Leather Leggings
|
||||
301 Leather Boots
|
||||
302 Chainmail Helmet
|
||||
303 Chainmail Chestplate
|
||||
304 Chainmail Leggings
|
||||
305 Chainmail Boots
|
||||
306 Iron Helmet
|
||||
307 Iron Chestplate
|
||||
308 Iron Leggings
|
||||
309 Iron Boots
|
||||
310 Diamond Helmet
|
||||
311 Diamond Chestplate
|
||||
312 Diamond Leggings
|
||||
313 Diamond Boots
|
||||
314 Gold Helmet
|
||||
315 Gold Chestplate
|
||||
316 Gold Leggings
|
||||
317 Gold Boots
|
||||
318 Flint
|
||||
319 Raw Porkchop
|
||||
320 Cooked Porkchop
|
||||
321 Painting
|
||||
322 Golden Apple
|
||||
322:1 Enchanted Golden Apple
|
||||
323 Sign
|
||||
324 Wooden Door
|
||||
325 Bucket
|
||||
326 Bucket (Water)
|
||||
327 Bucket (Lava)
|
||||
328 Minecart
|
||||
329 Saddle
|
||||
330 Iron Door
|
||||
331 Redstone Dust
|
||||
332 Snowball
|
||||
333 Boat
|
||||
334 Leather
|
||||
335 Bucket (Milk)
|
||||
336 Clay Brick
|
||||
337 Clay
|
||||
338 Sugar Cane
|
||||
339 Paper
|
||||
340 Book
|
||||
341 Slime Ball
|
||||
342 Minecart (Storage)
|
||||
343 Minecart (Powered)
|
||||
344 Egg
|
||||
345 Compass
|
||||
346 Fishing Rod
|
||||
347 Watch
|
||||
348 Glowstone Dust
|
||||
349 Raw Fish
|
||||
349:1 Raw Salmon
|
||||
349:2 Clownfish
|
||||
349:3 Pufferfish
|
||||
350 Cooked Fish
|
||||
350:1 Cooked Salmon
|
||||
350:2 Clownfish
|
||||
350:3 Pufferfish
|
||||
351 Ink Sack
|
||||
351:1 Rose Red Dye
|
||||
351:2 Cactus Green Dye
|
||||
351:3 Cocoa Bean
|
||||
351:4 Lapis Lazuli
|
||||
351:5 Purple Dye
|
||||
351:6 Cyan Dye
|
||||
351:7 Light Gray Dye
|
||||
351:8 Gray Dye
|
||||
351:9 Pink Dye
|
||||
351:10 Lime Dye
|
||||
351:11 Dandelion Yellow Dye
|
||||
351:12 Light Blue Dye
|
||||
351:13 Magenta Dye
|
||||
351:14 Orange Dye
|
||||
351:15 Bone Meal
|
||||
352 Bone
|
||||
353 Sugar
|
||||
354 Cake
|
||||
355 Bed
|
||||
356 Redstone Repeater
|
||||
357 Cookie
|
||||
358 Map
|
||||
359 Shears
|
||||
360 Melon (Slice)
|
||||
361 Pumpkin Seeds
|
||||
362 Melon Seeds
|
||||
363 Raw Beef
|
||||
364 Steak
|
||||
365 Raw Chicken
|
||||
366 Cooked Chicken
|
||||
367 Rotten Flesh
|
||||
368 Ender Pearl
|
||||
369 Blaze Rod
|
||||
370 Ghast Tear
|
||||
371 Gold Nugget
|
||||
372 Nether Wart Seeds
|
||||
373 Water Bottle
|
||||
373:16 Awkward Potion
|
||||
373:32 Thick Potion
|
||||
373:64 Mundane Potion
|
||||
373:8193 Regeneration Potion (0:45)
|
||||
373:8194 Swiftness Potion (3:00)
|
||||
373:8195 Fire Resistance Potion (3:00)
|
||||
373:8196 Poison Potion (0:45)
|
||||
373:8197 Healing Potion
|
||||
373:8198 Night Vision Potion (3:00)
|
||||
373:8200 Weakness Potion (1:30)
|
||||
373:8201 Strength Potion (3:00)
|
||||
373:8202 Slowness Potion (1:30)
|
||||
373:8204 Harming Potion
|
||||
373:8205 Water Breathing Potion (3:00)
|
||||
373:8206 Invisibility Potion (3:00)
|
||||
373:8225 Regeneration Potion II (0:22)
|
||||
373:8226 Swiftness Potion II (1:30)
|
||||
373:8228 Poison Potion II (0:22)
|
||||
373:8229 Healing Potion II
|
||||
373:8233 Strength Potion II (1:30)
|
||||
373:8236 Harming Potion II
|
||||
373:8257 Regeneration Potion (2:00)
|
||||
373:8258 Swiftness Potion (8:00)
|
||||
373:8259 Fire Resistance Potion (8:00)
|
||||
373:8260 Poison Potion (2:00)
|
||||
373:8262 Night Vision Potion (8:00)
|
||||
373:8264 Weakness Potion (4:00)
|
||||
373:8265 Strength Potion (8:00)
|
||||
373:8266 Slowness Potion (4:00)
|
||||
373:8269 Water Breathing Potion (8:00)
|
||||
373:8270 Invisibility Potion (8:00)
|
||||
373:8289 Regeneration Potion II (1:00)
|
||||
373:8290 Swiftness Potion II (4:00)
|
||||
373:8292 Poison Potion II (1:00)
|
||||
373:8297 Strength Potion II (4:00)
|
||||
373:16385 Regeneration Splash (0:33)
|
||||
373:16386 Swiftness Splash (2:15)
|
||||
373:16387 Fire Resistance Splash (2:15)
|
||||
373:16388 Poison Splash (0:33)
|
||||
373:16389 Healing Splash
|
||||
373:16390 Night Vision Splash (2:15)
|
||||
373:16392 Weakness Splash (1:07)
|
||||
373:16393 Strength Splash (2:15)
|
||||
373:16394 Slowness Splash (1:07)
|
||||
373:16396 Harming Splash
|
||||
373:16397 Breathing Splash (2:15)
|
||||
373:16398 Invisibility Splash (2:15)
|
||||
373:16417 Regeneration Splash II (0:16)
|
||||
373:16418 Swiftness Splash II (1:07)
|
||||
373:16420 Poison Splash II (0:16)
|
||||
373:16421 Healing Splash II
|
||||
373:16425 Strength Splash II (1:07)
|
||||
373:16428 Harming Splash II
|
||||
373:16449 Regeneration Splash (1:30)
|
||||
373:16450 Swiftness Splash (6:00)
|
||||
373:16451 Fire Resistance Splash (6:00)
|
||||
373:16452 Poison Splash (1:30)
|
||||
373:16454 Night Vision Splash (6:00)
|
||||
373:16456 Weakness Splash (3:00)
|
||||
373:16457 Strength Splash (6:00)
|
||||
373:16458 Slowness Splash (3:00)
|
||||
373:16461 Breathing Splash (6:00)
|
||||
373:16462 Invisibility Splash (6:00)
|
||||
373:16481 Regeneration Splash II (0:45)
|
||||
373:16482 Swiftness Splash II (3:00)
|
||||
373:16484 Poison Splash II (0:45)
|
||||
373:16489 Strength Splash II (3:00)
|
||||
374 Glass Bottle
|
||||
375 Spider Eye
|
||||
376 Fermented Spider Eye
|
||||
377 Blaze Powder
|
||||
378 Magma Cream
|
||||
379 Brewing Stand
|
||||
380 Cauldron
|
||||
381 Eye of Ender
|
||||
382 Glistering Melon (Slice)
|
||||
383:50 Spawn Egg (Creeper)
|
||||
383:51 Spawn Egg (Skeleton)
|
||||
383:52 Spawn Egg (Spider)
|
||||
383:54 Spawn Egg (Zombie)
|
||||
383:55 Spawn Egg (Slime)
|
||||
383:56 Spawn Egg (Ghast)
|
||||
383:57 Spawn Egg (Zombie Pigmen)
|
||||
383:58 Spawn Egg (Endermen)
|
||||
383:59 Spawn Egg (Cave Spider)
|
||||
383:60 Spawn Egg (Silverfish)
|
||||
383:61 Spawn Egg (Blaze)
|
||||
383:62 Spawn Egg (Magma Cube)
|
||||
383:65 Spawn Egg (Bat)
|
||||
383:66 Spawn Egg (Witch)
|
||||
383:90 Spawn Egg (Pig)
|
||||
383:91 Spawn Egg (Sheep)
|
||||
383:92 Spawn Egg (Cow)
|
||||
383:93 Spawn Egg (Chicken)
|
||||
383:94 Spawn Egg (Squid)
|
||||
383:95 Spawn Egg (Wolf)
|
||||
383:96 Spawn Egg (Mooshroom)
|
||||
383:98 Spawn Egg (Ocelot)
|
||||
383:100 Spawn Egg (Horse)
|
||||
383:120 Spawn Egg (Villager)
|
||||
384 Bottle of Enchanting
|
||||
385 Fire Charge
|
||||
386 Book and Quill
|
||||
387 Written Book
|
||||
388 Emerald
|
||||
389 Item Frame
|
||||
390 Flower Pot
|
||||
391 Carrot
|
||||
392 Potato
|
||||
393 Baked Potato
|
||||
394 Poisonous Potato
|
||||
395 Empty Map
|
||||
396 Golden Carrot
|
||||
397 Head (Skeleton)
|
||||
397:1 Head (Wither)
|
||||
397:2 Head (Zombie)
|
||||
397:3 Head (Steve)
|
||||
397:4 Head (Creeper)
|
||||
398 Carrot on a Stick
|
||||
399 Nether Star
|
||||
400 Pumpkin Pie
|
||||
401 Firework Rocket
|
||||
402 Firework Star
|
||||
403 Enchanted Book
|
||||
404 Redstone Comparator
|
||||
405 Nether Brick (Item)
|
||||
406 Nether Quartz
|
||||
407 Minecart (TNT)
|
||||
408 Minecart (Hopper)
|
||||
417 Iron Horse Armor
|
||||
418 Gold Horse Armor
|
||||
419 Diamond Horse Armor
|
||||
420 Lead
|
||||
421 Name Tag
|
||||
422 Minecart (Command Block)
|
||||
2256 Music Disk (13)
|
||||
2257 Music Disk (Cat)
|
||||
2258 Music Disk (Blocks)
|
||||
2259 Music Disk (Chirp)
|
||||
2260 Music Disk (Far)
|
||||
2261 Music Disk (Mall)
|
||||
2262 Music Disk (Mellohi)
|
||||
2263 Music Disk (Stal)
|
||||
2264 Music Disk (Strad)
|
||||
2265 Music Disk (Ward)
|
||||
2266 Music Disk (11)
|
||||
2267 Music Disk (Wait)
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"templates": [
|
||||
"rips off {user}'s {limbs} and leaves them to die.",
|
||||
"grabs {user}'s head and rips it clean off their body.",
|
||||
"grabs a {gun} and riddles {user}'s body with bullets.",
|
||||
"gags and ties {user} then throws them off a {tall_thing}.",
|
||||
"crushes {user} with a huge spiked {spiked_thing}.",
|
||||
"glares at {user} until they die of boredom.",
|
||||
"stabs {user} in the heart a few times with a {weapon_stab}.",
|
||||
"rams a {weapon_explosive} up {user}'s ass and lets off a few rounds.",
|
||||
"crushes {user}'s skull in with a {weapon_crush}.",
|
||||
"unleashes the armies of Isengard on {user}.",
|
||||
"gags and ties {user} then throws them off a {tall_thing} to their death.",
|
||||
"reaches out and punches right through {user}'s chest.",
|
||||
"slices {user}'s limbs off with a {weapon_slice}.",
|
||||
"throws {user} to Cthulu and watches them get ripped to shreds.",
|
||||
"feeds {user} to an owlbear who then proceeds to maul them violently.",
|
||||
"turns {user} into a snail and covers then in salt.",
|
||||
"snacks on {user}'s dismembered body.",
|
||||
"stuffs {bomb} up {user}'s ass and waits for it to go off.",
|
||||
"puts {user} into a sack, throws the sack in the river, and hurls the river into space.",
|
||||
"goes bowling with {user}'s bloody disembodied head.",
|
||||
"sends {user} to /dev/null!",
|
||||
"feeds {user} coke and mentos till they violently explode."
|
||||
],
|
||||
"parts": {
|
||||
"gun": [
|
||||
"AK47",
|
||||
"machine gun",
|
||||
"automatic pistol",
|
||||
"Uzi"
|
||||
],
|
||||
"limbs": [
|
||||
"legs",
|
||||
"arms",
|
||||
"limbs"
|
||||
],
|
||||
"weapon_stab": [
|
||||
"knife",
|
||||
"shard of glass",
|
||||
"sword blade",
|
||||
"butchers knife",
|
||||
"corkscrew"
|
||||
],
|
||||
"weapon_slice": [
|
||||
"sharpened katana",
|
||||
"chainsaw",
|
||||
"polished axe"
|
||||
],
|
||||
"weapon_crush": [
|
||||
"spiked mace",
|
||||
"baseball bat",
|
||||
"wooden club",
|
||||
"massive steel ball",
|
||||
"heavy iron rod"
|
||||
],
|
||||
"weapon_explosive": [
|
||||
"rocket launcher",
|
||||
"grenade launcher",
|
||||
"napalm launcher"
|
||||
],
|
||||
"tall_thing": [
|
||||
"bridge",
|
||||
"tall building",
|
||||
"cliff",
|
||||
"mountain"
|
||||
],
|
||||
"spiked_thing": [
|
||||
"boulder",
|
||||
"rock",
|
||||
"barrel of rocks"
|
||||
],
|
||||
"bomb": [
|
||||
"a bomb",
|
||||
"some TNT",
|
||||
"a bunch of C4"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
smacks {user} in the face with a burlap sack full of broken glass.
|
||||
swaps {user}'s shampoo with glue.
|
||||
installs Windows on {user}'s computer.
|
||||
installs Windows Vista on {user}'s computer.
|
||||
forces {user} to use perl for 3 weeks.
|
||||
registers {user}'s name with 50 known spammers.
|
||||
resizes {user}'s console to 40x24.
|
||||
|
@ -37,10 +37,8 @@ takes away {user}'s internet connection.
|
|||
pushes {user} past the Shoe Event Horizon.
|
||||
counts '1, 2, 5... er... 3!' and hurls the Holy Handgrenade Of Antioch at {user}.
|
||||
puts {user} in a nest of camel spiders.
|
||||
makes {user} read slashdot at -1.
|
||||
puts 'alias vim=emacs' in {user}'s /etc/profile.
|
||||
uninstalls every web browser from {user}'s system.
|
||||
locks {user} in the Chateau d'If.
|
||||
signs {user} up for getting hit on the head lessons.
|
||||
makes {user} try to set up a Lexmark printer.
|
||||
fills {user}'s eyedrop bottle with lime juice.
|
||||
|
@ -53,13 +51,10 @@ puts sugar between {user}'s bedsheets.
|
|||
pours sand into {user}'s breakfast.
|
||||
mixes epoxy into {user}'s toothpaste.
|
||||
puts Icy-Hot in {user}'s lube container.
|
||||
straps {user} to a chair, and plays a endless low bitrate MP3 loop of "the world's most annoying sound" from "Dumb and Dumber".
|
||||
tells Dr. Dre that {user} was talking smack.
|
||||
forces {user} to use a Commodore 64 for all their word processing.
|
||||
puts {user} in a room with several heavily armed manic depressives.
|
||||
makes {user} watch reruns of "Blue's Clues".
|
||||
puts lye in {user}'s coffee.
|
||||
introduces {user} to the clue-by-four.
|
||||
tattoos the Windows symbol on {user}'s ass.
|
||||
lets Borg have his way with {user}.
|
||||
signs {user} up for line dancing classes at the local senior center.
|
||||
|
@ -94,12 +89,11 @@ signs {user} up for the Iowa State Ferret Legging Championship.
|
|||
attempts to hotswap {user}'s RAM.
|
||||
dragon punches {user}.
|
||||
puts railroad spikes into {user}'s side.
|
||||
replaces {user}'s Astroglide with JB Weld.
|
||||
replaces {user}'s lubricant with liquid weld.
|
||||
replaces {user}'s stress pills with rat poison pellets.
|
||||
replaces {user}'s crotch itch cream with Nair.
|
||||
replaces {user}'s itch cream with hair removal cream.
|
||||
does the Australian Death Grip on {user}.
|
||||
dances upon the grave of {user}'s ancestors.
|
||||
farts in {user}'s general direction.
|
||||
farts loudly in {user}'s general direction.
|
||||
flogs {user} with stinging nettle.
|
||||
intoduces {user} to the Knights who say Ni.
|
||||
hands {user} a poison ivy joint.
|
|
@ -1,7 +1,7 @@
|
|||
//Minecraft Recipes List
|
||||
//Created by _303
|
||||
//Obtained from https://github.com/ClouDev/CloudBot/blob/develop/plugins/data/recipes.txt
|
||||
//Edited by _frozen
|
||||
//Edited by CHCMATT for Minecraft version: 1.7.4
|
||||
//
|
||||
//Summary of Use: Each column is seperated by a comma (,) and rows by a vertical bar (|). Order of Recipes & Categories taken from
|
||||
//www.minecraftwiki.net/wiki/Crafting for easier updating in the future (The Future!)
|
||||
|
@ -21,7 +21,10 @@
|
|||
1x Block of Gold: Gold Ingot, Gold Ingot, Gold Ingot | Gold Ingot, Gold Ingot, Gold Ingot | Gold Ingot, Gold Ingot, Gold Ingot
|
||||
1x Block of Iron: Iron Ingot, Iron Ingot, Iron Ingot | Iron Ingot, Iron Ingot, Iron Ingot | Iron Ingot, Iron Ingot, Iron Ingot
|
||||
1x Block of Diamond: Diamond, Diamond, Diamond | Diamond, Diamond, Diamond | Diamond, Diamond, Diamond
|
||||
1x Block of Coal: Coal, Coal, Coal | Coal, Coal, Coal | Coal, Coal, Coal
|
||||
1x Block of Redstone: Redstone Dust, Redstone Dust, Redstone Dust | Redstone Dust, Redstone Dust, Redstone Dust | Redstone Dust, Redstone Dust, Redstone Dust
|
||||
1x Lapis Lazuli Block: Lapis Lazuli, Lapis Lazuli, Lapis Lazuli | Lapis Lazuli, Lapis Lazuli, Lapis Lazuli | Lapis Lazuli, Lapis Lazuli, Lapis Lazuli
|
||||
1x Emerald Block: Emerald, Emerald, Emerald | Emerald, Emerald, Emerald | Emerald, Emerald, Emerald
|
||||
1x Glowstone: Glowstone Dust, Glowstone Dust | Glowstone Dust, Glowstone Dust
|
||||
1x Wool: String, String | String, String
|
||||
1x TNT: Gunpowder, Sand, Gunpowder | Sand, Gunpowder, Sand | Gunpowder, Sand, Gunpowder
|
||||
|
@ -117,6 +120,7 @@
|
|||
6x Powered Rail: Gold Ingot, None, Gold Ingot | Gold Ingot, Stick, Gold Ingot | Gold Ingot, Redstone, Gold Ingot
|
||||
6x Detector Rail: Iron Ingot, None, Iron Ingot | Iron Ingot, Pressure Plate, Iron Ingot | Iron Ingot, Redstone, Iron Ingot
|
||||
1x Boat: Wooden Planks, None, Wooden Planks | Wooden Planks, Wooden Planks, Wooden Planks
|
||||
1x Carrot On A Stick: Fishing Rod | None, Carrot
|
||||
//
|
||||
//Mechanism Recipes
|
||||
//
|
||||
|
@ -125,7 +129,8 @@
|
|||
2x Trapdoor: Wooden Planks, Wooden Planks, Wooden Planks | Wooden Planks, Wooden Planks, Wooden Planks
|
||||
1x Stone Pressure Plate: Stone, Stone
|
||||
1x Wooden Pressure Plate: Wooden Planks, Wooden Planks
|
||||
1x Button: Stone | Stone
|
||||
1x Stone Button: Stone
|
||||
1x Wooden Button: Wooden Planks
|
||||
1x Redstone Torch: Redstone | Stick
|
||||
1x Lever: Stick | Cobblestone
|
||||
1x Note Block: Wooden Planks, Wooden Planks, Wooden Planks | Wooden Planks, Redstone, Wooden Planks | Wooden Planks, Wooden Planks, Wooden Planks
|
||||
|
@ -133,8 +138,13 @@
|
|||
1x Dispenser: Cobblestone, Cobblestone, Cobblestone | Cobblestone, Bow, Cobblestone | Cobblestone, Redstone, Cobblestone
|
||||
1x Redstone Repeater: Redstone Torch, Redstone, Redstone Torch | Stone, Stone, Stone
|
||||
1x Piston: Wooden Planks, Wooden Planks, Wooden Planks | Cobblestone, Iron Ingot, Cobblestone | Cobblestone, Redstone, Cobblestone
|
||||
1x Sticky Piston: none, slime ball, none | none, piston, none
|
||||
1x Redstone Lamp: none, redstone dust, none | redstone dust, glowstone block, redstone | none, redstone dust, none
|
||||
1x Sticky Piston: Slime Ball | Piston
|
||||
1x Redstone Lamp: None, Redstone Dust, None | Redstone Dust, Glowstone Block, Redstone Dust | None, Redstone Dust, None
|
||||
1x Trapped Chest: Chest, Tripwire Hook
|
||||
1x Dropper: Cobblestone, Cobblestone, Cobblestone | Cobblestone, None, Cobblestone | Cobblestone, Redstone Dust, Cobblestone
|
||||
1x Weighted Pressure Plate (Heavy): Iron Ingot, Iron Ingot
|
||||
1x Weighted Pressure Plate (Light): Gold Ingot, Gold Ingot
|
||||
2x Tripwire Hook: Iron Ingot | Stick | Wooden Planks
|
||||
//
|
||||
//Food Recipes
|
||||
//
|
||||
|
@ -169,6 +179,11 @@
|
|||
9x Gold Nugget: Gold Ingot
|
||||
1x Gold Ingot: Gold Nugget, Gold Nugget, Gold Nugget | Gold Nugget, Gold Nugget, Gold Nugget | Gold Nugget, Gold Nugget, Gold Nugget
|
||||
1x Eye of Ender: Ender Pearl | Blaze Powder
|
||||
1x Item Frame: Stick, Stick, Stick | Stick, Leather, Stick | Stick, Stick, Stick
|
||||
1x Anvil: Block of Iron, Block of Iron, Block of Iron | None, Iron Ingot, None | Iron Ingot, Iron Ingot, Iron Ingot
|
||||
1x Ender Chest: Obsidian, Obsidian, Obsidian | Osbidian, Eye of Ender, Obsidian | Obsidian, Obsidian, Obsidian
|
||||
1x Flower Pot: Brick, None, Brick | None, Brick, None
|
||||
2x Lead: None, String, String | None, Slime Ball, String | String, None, None
|
||||
//
|
||||
//Dye Recipes
|
||||
//
|
||||
|
@ -214,4 +229,41 @@
|
|||
1x Fermented Spider Eye: Spider Eye | Brown Mushroom, Sugar
|
||||
1x Glistering Melon: Melon Slice, Gold Nugget
|
||||
9x Gold Nugget: Gold Ingot
|
||||
1x Enchantment Table: None, Book, None | Diamond, Obsidian, Diamond | Obsidian, Obsidian, Obsidian
|
||||
1x Enchantment Table: None, Book, None | Diamond, Obsidian, Diamond | Obsidian, Obsidian, Obsidian
|
||||
//
|
||||
//Stained Glass Recipes
|
||||
//
|
||||
8x White Stained Glass: Glass, Glass, Glass | Glass, Bone Meal, Glass | Glass, Glass, Glass
|
||||
8x Orange Stained Glass: Glass, Glass, Glass | Glass, Orange Dye, Glass | Glass, Glass, Glass
|
||||
8x Magenta Stained Glass: Glass, Glass, Glass | Glass, Magenta Dye, Glass | Glass, Glass, Glass
|
||||
8x Light Blue Stained Glass: Glass, Glass, Glass | Glass, Light Blue Dye, Glass | Glass, Glass, Glass
|
||||
8x Yellow Stained Glass: Glass, Glass, Glass | Glass, Dandelion Yellow, Glass | Glass, Glass, Glass
|
||||
8x Lime Stained Glass: Glass, Glass, Glass | Glass, Lime Dye, Glass | Glass, Glass, Glass
|
||||
8x Pink Stained Glass: Glass, Glass, Glass | Glass, Pink Dye, Glass | Glass, Glass, Glass
|
||||
8x Gray Stained Glass: Glass, Glass, Glass | Glass, Gray Dye, Glass | Glass, Glass, Glass
|
||||
8x Light Gray Stained Glass: Glass, Glass, Glass | Glass, Light Gray Dye, Glass | Glass, Glass, Glass
|
||||
8x Cyan Stained Glass: Glass, Glass, Glass | Glass, Cyan Dye, Glass | Glass, Glass, Glass
|
||||
8x Purple Stained Glass: Glass, Glass, Glass | Glass, Purple Dye, Glass | Glass, Glass, Glass
|
||||
8x Blue Stained Glass: Glass, Glass, Glass | Glass, Lapis Lazuli, Glass | Glass, Glass, Glass
|
||||
8x Brown Stained Glass: Glass, Glass, Glass | Glass, Cocoa Beans, Glass | Glass, Glass, Glass
|
||||
8x Green Stained Glass: Glass, Glass, Glass | Glass, Cactus Green, Glass | Glass, Glass, Glass
|
||||
8x Red Stained Glass: Glass, Glass, Glass | Glass, Rose Red, Glass | Glass, Glass, Glass
|
||||
8x Black Stained Glass: Glass, Glass, Glass | Glass, Inc Sac, Glass | Glass, Glass, Glass
|
||||
//
|
||||
//Stained Glass Panes
|
||||
//
|
||||
16x White Stained Glass Panes: White Stained Glass, White Stained Glass, White Stained Glass | White Stained Glass, White Stained Glass, White Stained Glass
|
||||
16x Orange Stained Glass Panes: Orange Stained Glass, Orange Stained Glass, Orange Stained Glass | Orange Stained Glass, Orange Stained Glass, Orange Stained Glass
|
||||
16x Magenta Stained Glass Panes: Magenta Stained Glass, Magenta Stained Glass, Magenta Stained Glass | Magenta Stained Glass, Magenta Stained Glass, Magenta Stained Glass
|
||||
16x Light Blue Stained Glass Panes: Light Blue Stained Glass, Light Blue Stained Glass, Light Blue Stained Glass | Light Blue Stained Glass, Light Blue Stained Glass, Light Blue Stained Glass
|
||||
16x Yellow Stained Glass Panes: Yellow Stained Glass, Yellow Stained Glass, Yellow Stained Glass | Yellow Stained Glass, Yellow Stained Glass, Yellow Stained Glass
|
||||
16x Lime Stained Glass Panes: Lime Stained Glass, Lime Stained Glass, Lime Stained Glass | Lime Stained Glass, Lime Stained Glass, Lime Stained Glass
|
||||
16x Pink Stained Glass Panes: Pink Stained Glass, Pink Stained Glass, Pink Stained Glass | Pink Stained Glass, Pink Stained Glass, Pink Stained Glass
|
||||
16x Gray Stained Glass Panes: Gray Stained Glass, Gray Stained Glass, Gray Stained Glass | Gray Stained Glass, Gray Stained Glass, Gray Stained Glass
|
||||
16x Light Gray Stained Glass Panes: Light Gray Stained Glass, Light Gray Stained Glass, Light Gray Stained Glass | Light Gray Stained Glass, Light Gray Stained Glass, Light Gray Stained Glass
|
||||
16x Cyan Stained Glass Panes: Cyan Stained Glass, Cyan Stained Glass, Cyan Stained Glass | Cyan Stained Glass, Cyan Stained Glass, Cyan Stained Glass
|
||||
16x Purple Stained Glass Panes: Purple Stained Glass, Purple Stained Glass, Purple Stained Glass | Purple Stained Glass, Purple Stained Glass, Purple Stained Glass
|
||||
16x Blue Stained Glass Panes: Blue Stained Glass, Blue Stained Glass, Blue Stained Glass | Blue Stained Glass, Blue Stained Glass, Blue Stained Glass
|
||||
16x Brown Stained Glass Panes: Brown Stained Glass, Brown Stained Glass, Brown Stained Glass | Brown Stained Glass, Brown Stained Glass, Brown Stained Glass
|
||||
16x Green Stained Glass Panes: Green Stained Glass, Green Stained Glass, Green Stained Glass | Green Stained Glass, Green Stained Glass, Green Stained Glass
|
||||
16x Black Stained Glass Panes: Black Stained Glass, Black Stained Glass, Black Stained Glass | Black Stained Glass, Black Stained Glass, Black Stained Glass
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"templates":[
|
||||
"{hits} {user} with a {item}.",
|
||||
"{hits} {user} around a bit with a {item}.",
|
||||
"{throws} a {item} at {user}.",
|
||||
"{throws} a few {item}s at {user}.",
|
||||
"grabs a {item} and {throws} it in {user}'s face.",
|
||||
"launches a {item} in {user}'s general direction.",
|
||||
"sits on {user}'s face while slamming a {item} into their crotch.",
|
||||
"starts slapping {user} silly with a {item}.",
|
||||
"holds {user} down and repeatedly {hits} them with a {item}.",
|
||||
"prods {user} with a {item}.",
|
||||
"picks up a {item} and {hits} {user} with it.",
|
||||
"ties {user} to a chair and {throws} a {item} at them.",
|
||||
"{hits} {user} {where} with a {item}.",
|
||||
"ties {user} to a pole and whips them with a {item}."
|
||||
],
|
||||
"parts": {
|
||||
"item":[
|
||||
"cast iron skillet",
|
||||
"large trout",
|
||||
"baseball bat",
|
||||
"wooden cane",
|
||||
"nail",
|
||||
"printer",
|
||||
"shovel",
|
||||
"pair of trousers",
|
||||
"CRT monitor",
|
||||
"diamond sword",
|
||||
"baguette",
|
||||
"physics textbook",
|
||||
"toaster",
|
||||
"portrait of Richard Stallman",
|
||||
"television",
|
||||
"mau5head",
|
||||
"five ton truck",
|
||||
"roll of duct tape",
|
||||
"book",
|
||||
"laptop",
|
||||
"old television",
|
||||
"sack of rocks",
|
||||
"rainbow trout",
|
||||
"cobblestone block",
|
||||
"lava bucket",
|
||||
"rubber chicken",
|
||||
"spiked bat",
|
||||
"gold block",
|
||||
"fire extinguisher",
|
||||
"heavy rock",
|
||||
"chunk of dirt"
|
||||
],
|
||||
"throws": [
|
||||
"throws",
|
||||
"flings",
|
||||
"chucks"
|
||||
],
|
||||
"hits": [
|
||||
"hits",
|
||||
"whacks",
|
||||
"slaps",
|
||||
"smacks"
|
||||
],
|
||||
"where": [
|
||||
"in the chest",
|
||||
"on the head",
|
||||
"on the bum"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -14,8 +14,8 @@ sign_re = re.compile(r'[+-]?(?:\d*d)?(?:\d+|F)', re.I)
|
|||
split_re = re.compile(r'([\d+-]*)d?(F|\d*)', re.I)
|
||||
|
||||
|
||||
def nrolls(count, n):
|
||||
"roll an n-sided die count times"
|
||||
def n_rolls(count, n):
|
||||
"""roll an n-sided die count times"""
|
||||
if n == "F":
|
||||
return [random.randint(-1, 1) for x in xrange(min(count, 100))]
|
||||
if n < 2: # it's a coin
|
||||
|
@ -28,16 +28,16 @@ def nrolls(count, n):
|
|||
return [random.randint(1, n) for x in xrange(count)]
|
||||
else: # fake it
|
||||
return [int(random.normalvariate(.5 * (1 + n) * count,
|
||||
(((n + 1) * (2 * n + 1) / 6. -
|
||||
(.5 * (1 + n)) ** 2) * count) ** .5))]
|
||||
(((n + 1) * (2 * n + 1) / 6. -
|
||||
(.5 * (1 + n)) ** 2) * count) ** .5))]
|
||||
|
||||
|
||||
@hook.command('roll')
|
||||
#@hook.regex(valid_diceroll, re.I)
|
||||
@hook.command
|
||||
def dice(inp):
|
||||
"dice <diceroll> -- Simulates dicerolls. Example of <diceroll>:" \
|
||||
" 'dice 2d20-d5+4 roll 2'. D20s, subtract 1D5, add 4"
|
||||
"""dice <dice roll> -- Simulates dice rolls. Example of <dice roll>:
|
||||
'dice 2d20-d5+4 roll 2'. D20s, subtract 1D5, add 4"""
|
||||
|
||||
try: # if inp is a re.match object...
|
||||
(inp, desc) = inp.groups()
|
||||
|
@ -49,7 +49,7 @@ def dice(inp):
|
|||
|
||||
spec = whitespace_re.sub('', inp)
|
||||
if not valid_diceroll_re.match(spec):
|
||||
return "Invalid diceroll"
|
||||
return "Invalid dice roll"
|
||||
groups = sign_re.findall(spec)
|
||||
|
||||
total = 0
|
||||
|
@ -59,7 +59,7 @@ def dice(inp):
|
|||
count, side = split_re.match(roll).groups()
|
||||
count = int(count) if count not in " +-" else 1
|
||||
if side.upper() == "F": # fudge dice are basically 1d3-2
|
||||
for fudge in nrolls(count, "F"):
|
||||
for fudge in n_rolls(count, "F"):
|
||||
if fudge == 1:
|
||||
rolls.append("\x033+\x0F")
|
||||
elif fudge == -1:
|
||||
|
@ -73,17 +73,18 @@ def dice(inp):
|
|||
side = int(side)
|
||||
try:
|
||||
if count > 0:
|
||||
dice = nrolls(count, side)
|
||||
rolls += map(str, dice)
|
||||
total += sum(dice)
|
||||
d = n_rolls(count, side)
|
||||
rolls += map(str, d)
|
||||
total += sum(d)
|
||||
else:
|
||||
dice = nrolls(-count, side)
|
||||
rolls += [str(-x) for x in dice]
|
||||
total -= sum(dice)
|
||||
d = n_rolls(-count, side)
|
||||
rolls += [str(-x) for x in d]
|
||||
total -= sum(d)
|
||||
except OverflowError:
|
||||
# I have never seen this happen. If you make this happen, you win a cookie
|
||||
return "Thanks for overflowing a float, jerk >:["
|
||||
|
||||
if desc:
|
||||
return "%s: %d (%s)" % (desc.strip(), total, ", ".join(rolls))
|
||||
return "{}: {} ({})".format(desc.strip(), total, ", ".join(rolls))
|
||||
else:
|
||||
return "%d (%s)" % (total, ", ".join(rolls))
|
||||
return "{} ({})".format(total, ", ".join(rolls))
|
|
@ -1,5 +1,6 @@
|
|||
# Plugin by GhettoWizard and Scaevolus
|
||||
import re
|
||||
|
||||
from util import hook
|
||||
from util import http
|
||||
|
||||
|
@ -7,7 +8,7 @@ from util import http
|
|||
@hook.command('dictionary')
|
||||
@hook.command
|
||||
def define(inp):
|
||||
"define <word> -- Fetches definition of <word>."
|
||||
"""define <word> -- Fetches definition of <word>."""
|
||||
|
||||
url = 'http://ninjawords.com/'
|
||||
|
||||
|
@ -18,14 +19,14 @@ def define(inp):
|
|||
'//div[@class="example"]')
|
||||
|
||||
if not definition:
|
||||
return 'No results for ' + inp + ' :('
|
||||
return u'No results for {} :('.format(inp)
|
||||
|
||||
def format_output(show_examples):
|
||||
result = '%s: ' % h.xpath('//dt[@class="title-word"]/a/text()')[0]
|
||||
result = u'{}: '.format(h.xpath('//dt[@class="title-word"]/a/text()')[0])
|
||||
|
||||
correction = h.xpath('//span[@class="correct-word"]/text()')
|
||||
if correction:
|
||||
result = 'Definition for "%s": ' % correction[0]
|
||||
result = 'Definition for "{}": '.format(correction[0])
|
||||
|
||||
sections = []
|
||||
for section in definition:
|
||||
|
@ -40,7 +41,7 @@ def define(inp):
|
|||
for article in sections:
|
||||
result += article[0]
|
||||
if len(article) > 2:
|
||||
result += ' '.join('%d. %s' % (n + 1, section)
|
||||
result += u' '.join(u'{}. {}'.format(n + 1, section)
|
||||
for n, section in enumerate(article[1:]))
|
||||
else:
|
||||
result += article[1] + ' '
|
||||
|
@ -67,7 +68,7 @@ def define(inp):
|
|||
@hook.command('e')
|
||||
@hook.command
|
||||
def etymology(inp):
|
||||
"etymology <word> -- Retrieves the etymology of <word>."
|
||||
"""etymology <word> -- Retrieves the etymology of <word>."""
|
||||
|
||||
url = 'http://www.etymonline.com/index.php'
|
||||
|
||||
|
@ -76,7 +77,7 @@ def etymology(inp):
|
|||
etym = h.xpath('//dl')
|
||||
|
||||
if not etym:
|
||||
return 'No etymology found for ' + inp + ' :('
|
||||
return u'No etymology found for {} :('.format(inp)
|
||||
|
||||
etym = etym[0].text_content()
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
from util import hook, http
|
||||
|
||||
|
||||
@hook.command
|
||||
def domainr(inp):
|
||||
"""domainr <domain> - Use domain.nr's API to search for a domain, and similar domains."""
|
||||
try:
|
||||
data = http.get_json('http://domai.nr/api/json/search?q=' + inp)
|
||||
except (http.URLError, http.HTTPError) as e:
|
||||
return "Unable to get data for some reason. Try again later."
|
||||
if data['query'] == "":
|
||||
return "An error occurred: {status} - {message}".format(**data['error'])
|
||||
domains = ""
|
||||
for domain in data['results']:
|
||||
domains += ("\x034" if domain['availability'] == "taken" else (
|
||||
"\x033" if domain['availability'] == "available" else "\x031")) + domain['domain'] + "\x0f" + domain[
|
||||
'path'] + ", "
|
||||
return "Domains: " + domains
|
|
@ -5,7 +5,7 @@ from util import hook, http
|
|||
|
||||
@hook.command
|
||||
def down(inp):
|
||||
"down <url> -- Checks if the site at <url> is up or down."
|
||||
"""down <url> -- Checks if the site at <url> is up or down."""
|
||||
|
||||
if 'http://' not in inp:
|
||||
inp = 'http://' + inp
|
||||
|
@ -15,6 +15,6 @@ def down(inp):
|
|||
# http://mail.python.org/pipermail/python-list/2006-December/589854.html
|
||||
try:
|
||||
http.get(inp, get_method='HEAD')
|
||||
return inp + ' seems to be up'
|
||||
return '{} seems to be up'.format(inp)
|
||||
except http.URLError:
|
||||
return inp + ' seems to be down'
|
||||
return '{} seems to be down'.format(inp)
|
|
@ -1,4 +1,7 @@
|
|||
from util import hook, http
|
||||
import re
|
||||
|
||||
from util import hook, http, text
|
||||
|
||||
|
||||
api_url = "http://encyclopediadramatica.se/api.php?action=opensearch"
|
||||
ed_url = "http://encyclopediadramatica.se/"
|
||||
|
@ -6,10 +9,11 @@ ed_url = "http://encyclopediadramatica.se/"
|
|||
|
||||
@hook.command
|
||||
def drama(inp):
|
||||
"drama <phrase> -- Gets the first paragraph of" \
|
||||
" the Encyclopedia Dramatica article on <phrase>."
|
||||
"""drama <phrase> -- Gets the first paragraph of
|
||||
the Encyclopedia Dramatica article on <phrase>."""
|
||||
|
||||
j = http.get_json(api_url, search=inp)
|
||||
|
||||
if not j[1]:
|
||||
return "No results found."
|
||||
article_name = j[1][0].replace(' ', '_').encode('utf8')
|
||||
|
@ -20,8 +24,8 @@ def drama(inp):
|
|||
for p in page.xpath('//div[@id="bodyContent"]/p'):
|
||||
if p.text_content():
|
||||
summary = " ".join(p.text_content().splitlines())
|
||||
if len(summary) > 300:
|
||||
summary = summary[:summary.rfind(' ', 0, 300)] + "..."
|
||||
return "%s :: \x02%s\x02" % (summary, url)
|
||||
summary = re.sub("\[\d+\]", "", summary)
|
||||
summary = text.truncate_str(summary, 220)
|
||||
return "{} :: {}".format(summary, url)
|
||||
|
||||
return "Unknown Error."
|
|
@ -0,0 +1,23 @@
|
|||
import random
|
||||
|
||||
from util import hook, text
|
||||
|
||||
|
||||
color_codes = {
|
||||
"<r>": "\x02\x0305",
|
||||
"<g>": "\x02\x0303",
|
||||
"<y>": "\x02"
|
||||
}
|
||||
|
||||
with open("plugins/data/8ball_responses.txt") as f:
|
||||
responses = [line.strip() for line in
|
||||
f.readlines() if not line.startswith("//")]
|
||||
|
||||
|
||||
@hook.command('8ball')
|
||||
def eightball(inp, action=None):
|
||||
"""8ball <question> -- The all knowing magic eight ball,
|
||||
in electronic form. Ask and it shall be answered!"""
|
||||
|
||||
magic = text.multiword_replace(random.choice(responses), color_codes)
|
||||
action("shakes the magic 8 ball... {}".format(magic))
|
|
@ -0,0 +1,105 @@
|
|||
import os
|
||||
import base64
|
||||
import json
|
||||
import hashlib
|
||||
|
||||
from Crypto import Random
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Protocol.KDF import PBKDF2
|
||||
|
||||
from util import hook
|
||||
|
||||
|
||||
# helper functions to pad and unpad a string to a specified block size
|
||||
# <http://stackoverflow.com/questions/12524994/encrypt-decrypt-using-pycrypto-aes-256>
|
||||
BS = AES.block_size
|
||||
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
|
||||
unpad = lambda s: s[0:-ord(s[-1])]
|
||||
|
||||
# helper functions to encrypt and encode a string with AES and base64
|
||||
encode_aes = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
|
||||
decode_aes = lambda c, s: unpad(c.decrypt(base64.b64decode(s)))
|
||||
|
||||
db_ready = False
|
||||
|
||||
|
||||
def db_init(db):
|
||||
"""check to see that our db has the the encryption table."""
|
||||
global db_ready
|
||||
if not db_ready:
|
||||
db.execute("create table if not exists encryption(encrypted, iv, "
|
||||
"primary key(encrypted))")
|
||||
db.commit()
|
||||
db_ready = True
|
||||
|
||||
|
||||
def get_salt(bot):
|
||||
"""generate an encryption salt if none exists, then returns the salt"""
|
||||
if not bot.config.get("random_salt", False):
|
||||
bot.config["random_salt"] = hashlib.md5(os.urandom(16)).hexdigest()
|
||||
json.dump(bot.config, open('config', 'w'), sort_keys=True, indent=2)
|
||||
return bot.config["random_salt"]
|
||||
|
||||
|
||||
@hook.command
|
||||
def encrypt(inp, bot=None, db=None, notice=None):
|
||||
"""encrypt <pass> <string> -- Encrypts <string> with <pass>. (<string> can only be decrypted using this bot)"""
|
||||
db_init(db)
|
||||
|
||||
split = inp.split(" ")
|
||||
|
||||
# if there is only one argument, return the help message
|
||||
if len(split) == 1:
|
||||
notice(encrypt.__doc__)
|
||||
return
|
||||
|
||||
# generate the key from the password and salt
|
||||
password = split[0]
|
||||
salt = get_salt(bot)
|
||||
key = PBKDF2(password, salt)
|
||||
|
||||
# generate the IV and encode it to store in the database
|
||||
iv = Random.new().read(AES.block_size)
|
||||
iv_encoded = base64.b64encode(iv)
|
||||
|
||||
# create the AES cipher and encrypt/encode the text with it
|
||||
text = " ".join(split[1:])
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
encoded = encode_aes(cipher, text)
|
||||
|
||||
# store the encoded text and IV in the DB for decoding later
|
||||
db.execute("insert or replace into encryption(encrypted, iv)"
|
||||
"values(?,?)", (encoded, iv_encoded))
|
||||
db.commit()
|
||||
|
||||
return encoded
|
||||
|
||||
|
||||
@hook.command
|
||||
def decrypt(inp, bot=None, db=None, notice=None):
|
||||
"""decrypt <pass> <string> -- Decrypts <string> with <pass>. (can only decrypt strings encrypted on this bot)"""
|
||||
if not db_ready:
|
||||
db_init(db)
|
||||
|
||||
split = inp.split(" ")
|
||||
|
||||
# if there is only one argument, return the help message
|
||||
if len(split) == 1:
|
||||
notice(decrypt.__doc__)
|
||||
return
|
||||
|
||||
# generate the key from the password and salt
|
||||
password = split[0]
|
||||
salt = get_salt(bot)
|
||||
key = PBKDF2(password, salt)
|
||||
|
||||
text = " ".join(split[1:])
|
||||
|
||||
# get the encoded IV from the database and decode it
|
||||
iv_encoded = db.execute("select iv from encryption where"
|
||||
" encrypted=?", (text,)).fetchone()[0]
|
||||
iv = base64.b64decode(iv_encoded)
|
||||
|
||||
# create AES cipher, decode text, decrypt text, and unpad it
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
return decode_aes(cipher, text)
|
|
@ -2,8 +2,8 @@ from util import hook, http, web
|
|||
|
||||
|
||||
@hook.command(autohelp=False)
|
||||
def fact(inp, say=False, nick=False):
|
||||
"fact -- Gets a random fact from OMGFACTS."
|
||||
def fact(inp):
|
||||
"""fact -- Gets a random fact from OMGFACTS."""
|
||||
|
||||
attempts = 0
|
||||
|
||||
|
@ -20,10 +20,10 @@ def fact(inp, say=False, nick=False):
|
|||
|
||||
response = soup.find('a', {'class': 'surprise'})
|
||||
link = response['href']
|
||||
fact = ''.join(response.find(text=True))
|
||||
fact_data = ''.join(response.find(text=True))
|
||||
|
||||
if fact:
|
||||
fact = fact.strip()
|
||||
if fact_data:
|
||||
fact_data = fact_data.strip()
|
||||
break
|
||||
else:
|
||||
if attempts > 2:
|
||||
|
@ -32,9 +32,6 @@ def fact(inp, say=False, nick=False):
|
|||
attempts += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
url = web.isgd(link)
|
||||
except (web.ShortenError, http.HTTPError):
|
||||
url = link
|
||||
url = web.try_isgd(link)
|
||||
|
||||
return "%s - %s" % (fact, url)
|
||||
return "{} - {}".format(fact_data, url)
|
|
@ -1,29 +1,34 @@
|
|||
# Written by Scaevolus 2010
|
||||
from util import hook, http, text, execute
|
||||
import string
|
||||
import sqlite3
|
||||
import re
|
||||
|
||||
from util import hook, http, text, pyexec
|
||||
|
||||
|
||||
re_lineends = re.compile(r'[\r\n]*')
|
||||
|
||||
db_ready = False
|
||||
|
||||
# some simple "shortcodes" for formatting purposes
|
||||
shortcodes = {
|
||||
'[b]': '\x02',
|
||||
'[/b]': '\x02',
|
||||
'[u]': '\x1F',
|
||||
'[/u]': '\x1F',
|
||||
'[i]': '\x16',
|
||||
'[/i]': '\x16'}
|
||||
'[b]': '\x02',
|
||||
'[/b]': '\x02',
|
||||
'[u]': '\x1F',
|
||||
'[/u]': '\x1F',
|
||||
'[i]': '\x16',
|
||||
'[/i]': '\x16'}
|
||||
|
||||
|
||||
def db_init(db):
|
||||
db.execute("create table if not exists mem(word, data, nick,"
|
||||
" primary key(word))")
|
||||
db.commit()
|
||||
global db_ready
|
||||
if not db_ready:
|
||||
db.execute("create table if not exists mem(word, data, nick,"
|
||||
" primary key(word))")
|
||||
db.commit()
|
||||
db_ready = True
|
||||
|
||||
|
||||
def get_memory(db, word):
|
||||
|
||||
row = db.execute("select data from mem where word=lower(?)",
|
||||
[word]).fetchone()
|
||||
if row:
|
||||
|
@ -32,11 +37,11 @@ def get_memory(db, word):
|
|||
return None
|
||||
|
||||
|
||||
@hook.command("r", adminonly=True)
|
||||
@hook.command(adminonly=True)
|
||||
def remember(inp, nick='', db=None, say=None, input=None, notice=None):
|
||||
"remember <word> [+]<data> -- Remembers <data> with <word>. Add +"
|
||||
" to <data> to append."
|
||||
@hook.command("r", permissions=["addfactoid"])
|
||||
@hook.command(permissions=["addfactoid"])
|
||||
def remember(inp, nick='', db=None, notice=None):
|
||||
"""remember <word> [+]<data> -- Remembers <data> with <word>. Add +
|
||||
to <data> to append."""
|
||||
db_init(db)
|
||||
|
||||
append = False
|
||||
|
@ -64,20 +69,18 @@ def remember(inp, nick='', db=None, say=None, input=None, notice=None):
|
|||
|
||||
if old_data:
|
||||
if append:
|
||||
notice("Appending \x02%s\x02 to \x02%s\x02" % (new_data, old_data))
|
||||
notice("Appending \x02{}\x02 to \x02{}\x02".format(new_data, old_data))
|
||||
else:
|
||||
notice('Remembering \x02%s\x02 for \x02%s\x02. Type ?%s to see it.'
|
||||
% (data, word, word))
|
||||
notice('Previous data was \x02%s\x02' % old_data)
|
||||
notice('Remembering \x02{}\x02 for \x02{}\x02. Type ?{} to see it.'.format(data, word, word))
|
||||
notice('Previous data was \x02{}\x02'.format(old_data))
|
||||
else:
|
||||
notice('Remembering \x02%s\x02 for \x02%s\x02. Type ?%s to see it.'
|
||||
% (data, word, word))
|
||||
notice('Remembering \x02{}\x02 for \x02{}\x02. Type ?{} to see it.'.format(data, word, word))
|
||||
|
||||
|
||||
@hook.command("f", adminonly=True)
|
||||
@hook.command(adminonly=True)
|
||||
def forget(inp, db=None, input=None, notice=None):
|
||||
"forget <word> -- Forgets a remembered <word>."
|
||||
@hook.command("f", permissions=["delfactoid"])
|
||||
@hook.command(permissions=["delfactoid"])
|
||||
def forget(inp, db=None, notice=None):
|
||||
"""forget <word> -- Forgets a remembered <word>."""
|
||||
|
||||
db_init(db)
|
||||
data = get_memory(db, inp)
|
||||
|
@ -95,7 +98,7 @@ def forget(inp, db=None, input=None, notice=None):
|
|||
|
||||
@hook.command
|
||||
def info(inp, notice=None, db=None):
|
||||
"info <factoid> -- Shows the source of a factoid."
|
||||
"""info <factoid> -- Shows the source of a factoid."""
|
||||
|
||||
db_init(db)
|
||||
|
||||
|
@ -109,8 +112,8 @@ def info(inp, notice=None, db=None):
|
|||
|
||||
|
||||
@hook.regex(r'^\? ?(.+)')
|
||||
def factoid(inp, say=None, db=None, bot=None, me=None, conn=None, input=None):
|
||||
"?<word> -- Shows what data is associated with <word>."
|
||||
def factoid(inp, message=None, db=None, bot=None, action=None, conn=None, input=None):
|
||||
"""?<word> -- Shows what data is associated with <word>."""
|
||||
try:
|
||||
prefix_on = bot.config["plugins"]["factoids"].get("prefix", False)
|
||||
except KeyError:
|
||||
|
@ -133,15 +136,10 @@ def factoid(inp, say=None, db=None, bot=None, me=None, conn=None, input=None):
|
|||
# factoid preprocessors
|
||||
if data.startswith("<py>"):
|
||||
code = data[4:].strip()
|
||||
variables = 'input="""%s"""; nick="%s"; chan="%s"; bot_nick="%s";' % (arguments.replace('"', '\\"'),
|
||||
input.nick, input.chan, input.conn.nick)
|
||||
result = execute.eval_py(variables + code)
|
||||
elif data.startswith("<url>"):
|
||||
url = data[5:].strip()
|
||||
try:
|
||||
result = http.get(url)
|
||||
except http.HttpError:
|
||||
result = "Could not fetch URL."
|
||||
variables = 'input="""{}"""; nick="{}"; chan="{}"; bot_nick="{}";'.format(arguments.replace('"', '\\"'),
|
||||
input.nick, input.chan,
|
||||
input.conn.nick)
|
||||
result = pyexec.eval_py(variables + code)
|
||||
else:
|
||||
result = data
|
||||
|
||||
|
@ -150,9 +148,15 @@ def factoid(inp, say=None, db=None, bot=None, me=None, conn=None, input=None):
|
|||
|
||||
if result.startswith("<act>"):
|
||||
result = result[5:].strip()
|
||||
me(result)
|
||||
action(result)
|
||||
elif result.startswith("<url>"):
|
||||
url = result[5:].strip()
|
||||
try:
|
||||
message(http.get(url))
|
||||
except http.HttpError:
|
||||
message("Could not fetch URL.")
|
||||
else:
|
||||
if prefix_on:
|
||||
say("\x02[%s]:\x02 %s" % (factoid_id, result))
|
||||
message("\x02[{}]:\x02 {}".format(factoid_id, result))
|
||||
else:
|
||||
say(result)
|
||||
message(result)
|
|
@ -0,0 +1,57 @@
|
|||
from urllib import quote_plus
|
||||
|
||||
from util import hook, http
|
||||
|
||||
|
||||
api_url = "http://api.fishbans.com/stats/{}/"
|
||||
|
||||
|
||||
@hook.command("bans")
|
||||
@hook.command
|
||||
def fishbans(inp):
|
||||
"""fishbans <user> -- Gets information on <user>s minecraft bans from fishbans"""
|
||||
user = inp.strip()
|
||||
|
||||
try:
|
||||
request = http.get_json(api_url.format(quote_plus(user)))
|
||||
except (http.HTTPError, http.URLError) as e:
|
||||
return "Could not fetch ban data from the Fishbans API: {}".format(e)
|
||||
|
||||
if not request["success"]:
|
||||
return "Could not fetch ban data for {}.".format(user)
|
||||
|
||||
user_url = "http://fishbans.com/u/{}/".format(user)
|
||||
ban_count = request["stats"]["totalbans"]
|
||||
|
||||
return "The user \x02{}\x02 has \x02{}\x02 ban(s). See detailed info " \
|
||||
"at {}".format(user, ban_count, user_url)
|
||||
|
||||
|
||||
@hook.command
|
||||
def bancount(inp):
|
||||
"""bancount <user> -- Gets a count of <user>s minecraft bans from fishbans"""
|
||||
user = inp.strip()
|
||||
|
||||
try:
|
||||
request = http.get_json(api_url.format(quote_plus(user)))
|
||||
except (http.HTTPError, http.URLError) as e:
|
||||
return "Could not fetch ban data from the Fishbans API: {}".format(e)
|
||||
|
||||
if not request["success"]:
|
||||
return "Could not fetch ban data for {}.".format(user)
|
||||
|
||||
user_url = "http://fishbans.com/u/{}/".format(user)
|
||||
services = request["stats"]["service"]
|
||||
|
||||
out = []
|
||||
for service, ban_count in services.items():
|
||||
if ban_count != 0:
|
||||
out.append("{}: \x02{}\x02".format(service, ban_count))
|
||||
else:
|
||||
pass
|
||||
|
||||
if not out:
|
||||
return "The user \x02{}\x02 has no bans.".format(user)
|
||||
else:
|
||||
return "Bans for \x02{}\x02: ".format(user) + ", ".join(out) + ". More info " \
|
||||
"at {}".format(user_url)
|
|
@ -8,9 +8,9 @@ def refresh_cache():
|
|||
soup = http.get_soup('http://www.fmylife.com/random/')
|
||||
|
||||
for e in soup.find_all('div', {'class': 'post article'}):
|
||||
id = int(e['id'])
|
||||
fml_id = int(e['id'])
|
||||
text = ''.join(e.find('p').find_all(text=True))
|
||||
fml_cache.append((id, text))
|
||||
fml_cache.append((fml_id, text))
|
||||
|
||||
# do an initial refresh of the cache
|
||||
refresh_cache()
|
||||
|
@ -18,12 +18,12 @@ refresh_cache()
|
|||
|
||||
@hook.command(autohelp=False)
|
||||
def fml(inp, reply=None):
|
||||
"fml -- Gets a random quote from fmyfife.com."
|
||||
"""fml -- Gets a random quote from fmyfife.com."""
|
||||
|
||||
# grab the last item in the fml cache and remove it
|
||||
id, text = fml_cache.pop()
|
||||
fml_id, text = fml_cache.pop()
|
||||
# reply with the fml we grabbed
|
||||
reply('(#%d) %s' % (id, text))
|
||||
reply('(#{}) {}'.format(fml_id, text))
|
||||
# refresh fml cache if its getting empty
|
||||
if len(fml_cache) < 3:
|
||||
refresh_cache()
|
|
@ -1,6 +1,8 @@
|
|||
from util import hook
|
||||
import random
|
||||
|
||||
from util import hook
|
||||
|
||||
|
||||
with open("plugins/data/fortunes.txt") as f:
|
||||
fortunes = [line.strip() for line in f.readlines()
|
||||
if not line.startswith("//")]
|
||||
|
@ -8,5 +10,5 @@ with open("plugins/data/fortunes.txt") as f:
|
|||
|
||||
@hook.command(autohelp=False)
|
||||
def fortune(inp):
|
||||
"fortune -- Fortune cookies on demand."
|
||||
"""fortune -- Fortune cookies on demand."""
|
||||
return random.choice(fortunes)
|
|
@ -0,0 +1,13 @@
|
|||
from util import hook, http, web
|
||||
from subprocess import check_output, CalledProcessError
|
||||
|
||||
@hook.command
|
||||
def freddycode(inp):
|
||||
"""freddycode <code> - Check if the Freddy Fresh code is correct."""
|
||||
|
||||
try:
|
||||
return "Freddy: '%s' ist %s" % (inp, \
|
||||
check_output(["/bin/freddycheck", inp]))
|
||||
except CalledProcessError as err:
|
||||
return "Freddy: Skript returned %s" % (str(err))
|
||||
|
|
@ -1,20 +1,36 @@
|
|||
from util import hook
|
||||
import os.path
|
||||
import pygeoip
|
||||
import json
|
||||
import gzip
|
||||
from StringIO import StringIO
|
||||
|
||||
# initalise geolocation database
|
||||
geo = pygeoip.GeoIP(os.path.abspath("./plugins/data/geoip.dat"))
|
||||
import pygeoip
|
||||
|
||||
from util import hook, http
|
||||
|
||||
|
||||
# load region database
|
||||
with open("./plugins/data/geoip_regions.json", "rb") as f:
|
||||
regions = json.loads(f.read())
|
||||
|
||||
if os.path.isfile(os.path.abspath("./plugins/data/GeoLiteCity.dat")):
|
||||
# initialise geolocation database
|
||||
geo = pygeoip.GeoIP(os.path.abspath("./plugins/data/GeoLiteCity.dat"))
|
||||
else:
|
||||
download = http.get("http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz")
|
||||
string_io = StringIO(download)
|
||||
geoip_file = gzip.GzipFile(fileobj=string_io, mode='rb')
|
||||
|
||||
output = open(os.path.abspath("./plugins/data/GeoLiteCity.dat"), 'wb')
|
||||
output.write(geoip_file.read())
|
||||
output.close()
|
||||
|
||||
geo = pygeoip.GeoIP(os.path.abspath("./plugins/data/GeoLiteCity.dat"))
|
||||
|
||||
|
||||
@hook.command
|
||||
def geoip(inp):
|
||||
"geoip <host/ip> -- Gets the location of <host/ip>"
|
||||
"""geoip <host/ip> -- Gets the location of <host/ip>"""
|
||||
|
||||
try:
|
||||
record = geo.record_by_name(inp)
|
||||
except:
|
||||
|
@ -35,4 +51,4 @@ def geoip(inp):
|
|||
data["cc"] = record["country_code"] or "N/A"
|
||||
data["country"] = record["country_name"] or "Unknown"
|
||||
data["city"] = record["city"] or "Unknown"
|
||||
return "\x02Country:\x02 {country} ({cc}), \x02City:\x02 {city}{region}".format(**data)
|
||||
return u"\x02Country:\x02 {country} ({cc}), \x02City:\x02 {city}{region}".format(**data)
|
|
@ -0,0 +1,120 @@
|
|||
import json
|
||||
import urllib2
|
||||
|
||||
from util import hook, http
|
||||
|
||||
|
||||
shortcuts = {"cloudbot": "ClouDev/CloudBot"}
|
||||
|
||||
|
||||
def truncate(msg):
|
||||
nmsg = msg.split()
|
||||
out = None
|
||||
x = 0
|
||||
for i in nmsg:
|
||||
if x <= 7:
|
||||
if out:
|
||||
out = out + " " + nmsg[x]
|
||||
else:
|
||||
out = nmsg[x]
|
||||
x += 1
|
||||
if x <= 7:
|
||||
return out
|
||||
else:
|
||||
return out + "..."
|
||||
|
||||
|
||||
@hook.command
|
||||
def ghissues(inp):
|
||||
"""ghissues username/repo [number] - Get specified issue summary, or open issue count """
|
||||
args = inp.split(" ")
|
||||
try:
|
||||
if args[0] in shortcuts:
|
||||
repo = shortcuts[args[0]]
|
||||
else:
|
||||
repo = args[0]
|
||||
url = "https://api.github.com/repos/{}/issues".format(repo)
|
||||
except IndexError:
|
||||
return "Invalid syntax. .github issues username/repo [number]"
|
||||
try:
|
||||
url += "/%s" % args[1]
|
||||
number = True
|
||||
except IndexError:
|
||||
number = False
|
||||
try:
|
||||
data = json.loads(http.open(url).read())
|
||||
print url
|
||||
if not number:
|
||||
try:
|
||||
data = data[0]
|
||||
except IndexError:
|
||||
print data
|
||||
return "Repo has no open issues"
|
||||
except ValueError:
|
||||
return "Invalid data returned. Check arguments (.github issues username/repo [number]"
|
||||
fmt = "Issue: #%s (%s) by %s: %s | %s %s" # (number, state, user.login, title, truncate(body), gitio.gitio(data.url))
|
||||
fmt1 = "Issue: #%s (%s) by %s: %s %s" # (number, state, user.login, title, gitio.gitio(data.url))
|
||||
number = data["number"]
|
||||
if data["state"] == "open":
|
||||
state = u"\x033\x02OPEN\x02\x0f"
|
||||
else:
|
||||
state = u"\x034\x02CLOSED\x02\x0f by {}".format(data["closed_by"]["login"])
|
||||
user = data["user"]["login"]
|
||||
title = data["title"]
|
||||
summary = truncate(data["body"])
|
||||
gitiourl = gitio(data["html_url"])
|
||||
if "Failed to get URL" in gitiourl:
|
||||
gitiourl = gitio(data["html_url"] + " " + repo.split("/")[1] + number)
|
||||
if summary == "":
|
||||
return fmt1 % (number, state, user, title, gitiourl)
|
||||
else:
|
||||
return fmt % (number, state, user, title, summary, gitiourl)
|
||||
|
||||
|
||||
@hook.command
|
||||
def gitio(inp):
|
||||
"""gitio <url> [code] -- Shorten Github URLs with git.io. [code] is
|
||||
a optional custom short code."""
|
||||
split = inp.split(" ")
|
||||
url = split[0]
|
||||
|
||||
try:
|
||||
code = split[1]
|
||||
except:
|
||||
code = None
|
||||
|
||||
# if the first 8 chars of "url" are not "https://" then append
|
||||
# "https://" to the url, also convert "http://" to "https://"
|
||||
if url[:8] != "https://":
|
||||
if url[:7] != "http://":
|
||||
url = "https://" + url
|
||||
else:
|
||||
url = "https://" + url[7:]
|
||||
url = 'url=' + str(url)
|
||||
if code:
|
||||
url = url + '&code=' + str(code)
|
||||
req = urllib2.Request(url='http://git.io', data=url)
|
||||
|
||||
# try getting url, catch http error
|
||||
try:
|
||||
f = urllib2.urlopen(req)
|
||||
except urllib2.HTTPError:
|
||||
return "Failed to get URL!"
|
||||
urlinfo = str(f.info())
|
||||
|
||||
# loop over the rows in urlinfo and pick out location and
|
||||
# status (this is pretty odd code, but urllib2.Request is weird)
|
||||
for row in urlinfo.split("\n"):
|
||||
if row.find("Status") != -1:
|
||||
status = row
|
||||
if row.find("Location") != -1:
|
||||
location = row
|
||||
|
||||
print status
|
||||
if not "201" in status:
|
||||
return "Failed to get URL!"
|
||||
|
||||
# this wont work for some reason, so lets ignore it ^
|
||||
|
||||
# return location, minus the first 10 chars
|
||||
return location[10:]
|
|
@ -1,4 +1,5 @@
|
|||
import random
|
||||
|
||||
from util import hook, http, text
|
||||
|
||||
|
||||
|
@ -13,28 +14,25 @@ def api_get(kind, query):
|
|||
@hook.command('gis')
|
||||
@hook.command
|
||||
def googleimage(inp):
|
||||
"gis <query> -- Returns first Google Image result for <query>."
|
||||
"""gis <query> -- Returns first Google Image result for <query>."""
|
||||
|
||||
parsed = api_get('images', inp)
|
||||
if not 200 <= parsed['responseStatus'] < 300:
|
||||
raise IOError('error searching for images: %d: %s' % ( \
|
||||
parsed['responseStatus'], ''))
|
||||
raise IOError('error searching for images: {}: {}'.format(parsed['responseStatus'], ''))
|
||||
if not parsed['responseData']['results']:
|
||||
return 'no images found'
|
||||
return random.choice(parsed['responseData']['results'][:10]) \
|
||||
['unescapedUrl']
|
||||
return random.choice(parsed['responseData']['results'][:10])['unescapedUrl']
|
||||
|
||||
|
||||
@hook.command('search')
|
||||
@hook.command('g')
|
||||
@hook.command
|
||||
def google(inp):
|
||||
"google <query> -- Returns first google search result for <query>."
|
||||
"""google <query> -- Returns first google search result for <query>."""
|
||||
|
||||
parsed = api_get('web', inp)
|
||||
if not 200 <= parsed['responseStatus'] < 300:
|
||||
raise IOError('error searching for pages: %d: %s' % (
|
||||
parsed['responseStatus'], ''))
|
||||
raise IOError('error searching for pages: {}: {}'.format(parsed['responseStatus'], ''))
|
||||
if not parsed['responseData']['results']:
|
||||
return 'No results found.'
|
||||
|
||||
|
@ -50,6 +48,4 @@ def google(inp):
|
|||
content = http.html.fromstring(content).text_content()
|
||||
content = text.truncate_str(content, 150)
|
||||
|
||||
out = '%s -- \x02%s\x02: "%s"' % (result['unescapedUrl'], title, content)
|
||||
|
||||
return out
|
||||
return u'{} -- \x02{}\x02: "{}"'.format(result['unescapedUrl'], title, content)
|
|
@ -0,0 +1,168 @@
|
|||
"""
|
||||
A Google API key is required and retrieved from the bot config file.
|
||||
Since December 1, 2011, the Google Translate API is a paid service only.
|
||||
"""
|
||||
|
||||
import htmlentitydefs
|
||||
import re
|
||||
|
||||
from util import hook, http
|
||||
|
||||
|
||||
max_length = 100
|
||||
|
||||
|
||||
########### from http://effbot.org/zone/re-sub.htm#unescape-html #############
|
||||
|
||||
|
||||
def unescape(text):
|
||||
def fixup(m):
|
||||
text = m.group(0)
|
||||
if text[:2] == "&#":
|
||||
# character reference
|
||||
try:
|
||||
if text[:3] == "&#x":
|
||||
return unichr(int(text[3:-1], 16))
|
||||
else:
|
||||
return unichr(int(text[2:-1]))
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
# named entity
|
||||
try:
|
||||
text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
|
||||
except KeyError:
|
||||
pass
|
||||
return text # leave as is
|
||||
|
||||
return re.sub("&#?\w+;", fixup, text)
|
||||
|
||||
##############################################################################
|
||||
|
||||
|
||||
def goog_trans(api_key, text, slang, tlang):
|
||||
url = 'https://www.googleapis.com/language/translate/v2'
|
||||
|
||||
if len(text) > max_length:
|
||||
return "This command only supports input of less then 100 characters."
|
||||
|
||||
if slang:
|
||||
parsed = http.get_json(url, key=api_key, q=text, source=slang, target=tlang, format="text")
|
||||
else:
|
||||
parsed = http.get_json(url, key=api_key, q=text, target=tlang, format="text")
|
||||
|
||||
#if not 200 <= parsed['responseStatus'] < 300:
|
||||
# raise IOError('error with the translation server: %d: %s' % (
|
||||
# parsed['responseStatus'], parsed['responseDetails']))
|
||||
if not slang:
|
||||
return unescape('(%(detectedSourceLanguage)s) %(translatedText)s' %
|
||||
(parsed['data']['translations'][0]))
|
||||
return unescape('%(translatedText)s' % parsed['data']['translations'][0])
|
||||
|
||||
|
||||
def match_language(fragment):
|
||||
fragment = fragment.lower()
|
||||
for short, _ in lang_pairs:
|
||||
if fragment in short.lower().split():
|
||||
return short.split()[0]
|
||||
|
||||
for short, full in lang_pairs:
|
||||
if fragment in full.lower():
|
||||
return short.split()[0]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@hook.command
|
||||
def translate(inp, bot=None):
|
||||
"""translate [source language [target language]] <sentence> -- translates
|
||||
<sentence> from source language (default autodetect) to target
|
||||
language (default English) using Google Translate"""
|
||||
|
||||
api_key = bot.config.get("api_keys", {}).get("googletranslate", None)
|
||||
if not api_key:
|
||||
return "This command requires a paid API key."
|
||||
|
||||
args = inp.split(u' ', 2)
|
||||
|
||||
try:
|
||||
if len(args) >= 2:
|
||||
sl = match_language(args[0])
|
||||
if not sl:
|
||||
return goog_trans(api_key, inp, '', 'en')
|
||||
if len(args) == 2:
|
||||
return goog_trans(api_key, args[1], sl, 'en')
|
||||
if len(args) >= 3:
|
||||
tl = match_language(args[1])
|
||||
if not tl:
|
||||
if sl == 'en':
|
||||
return 'unable to determine desired target language'
|
||||
return goog_trans(api_key, args[1] + ' ' + args[2], sl, 'en')
|
||||
return goog_trans(api_key, args[2], sl, tl)
|
||||
return goog_trans(api_key, inp, '', 'en')
|
||||
except IOError, e:
|
||||
return e
|
||||
|
||||
|
||||
lang_pairs = [
|
||||
("no", "Norwegian"),
|
||||
("it", "Italian"),
|
||||
("ht", "Haitian Creole"),
|
||||
("af", "Afrikaans"),
|
||||
("sq", "Albanian"),
|
||||
("ar", "Arabic"),
|
||||
("hy", "Armenian"),
|
||||
("az", "Azerbaijani"),
|
||||
("eu", "Basque"),
|
||||
("be", "Belarusian"),
|
||||
("bg", "Bulgarian"),
|
||||
("ca", "Catalan"),
|
||||
("zh-CN zh", "Chinese"),
|
||||
("hr", "Croatian"),
|
||||
("cs", "Czech"),
|
||||
("da", "Danish"),
|
||||
("nl", "Dutch"),
|
||||
("en", "English"),
|
||||
("et", "Estonian"),
|
||||
("tl", "Filipino"),
|
||||
("fi", "Finnish"),
|
||||
("fr", "French"),
|
||||
("gl", "Galician"),
|
||||
("ka", "Georgian"),
|
||||
("de", "German"),
|
||||
("el", "Greek"),
|
||||
("ht", "Haitian Creole"),
|
||||
("iw", "Hebrew"),
|
||||
("hi", "Hindi"),
|
||||
("hu", "Hungarian"),
|
||||
("is", "Icelandic"),
|
||||
("id", "Indonesian"),
|
||||
("ga", "Irish"),
|
||||
("it", "Italian"),
|
||||
("ja jp jpn", "Japanese"),
|
||||
("ko", "Korean"),
|
||||
("lv", "Latvian"),
|
||||
("lt", "Lithuanian"),
|
||||
("mk", "Macedonian"),
|
||||
("ms", "Malay"),
|
||||
("mt", "Maltese"),
|
||||
("no", "Norwegian"),
|
||||
("fa", "Persian"),
|
||||
("pl", "Polish"),
|
||||
("pt", "Portuguese"),
|
||||
("ro", "Romanian"),
|
||||
("ru", "Russian"),
|
||||
("sr", "Serbian"),
|
||||
("sk", "Slovak"),
|
||||
("sl", "Slovenian"),
|
||||
("es", "Spanish"),
|
||||
("sw", "Swahili"),
|
||||
("sv", "Swedish"),
|
||||
("th", "Thai"),
|
||||
("tr", "Turkish"),
|
||||
("uk", "Ukrainian"),
|
||||
("ur", "Urdu"),
|
||||
("vi", "Vietnamese"),
|
||||
("cy", "Welsh"),
|
||||
("yi", "Yiddish")
|
||||
]
|
|
@ -0,0 +1,22 @@
|
|||
from util import hook
|
||||
from urllib import unquote
|
||||
|
||||
@hook.command(autohelp=False)
|
||||
def googleurl(inp, db=None, nick=None):
|
||||
"""googleurl [nickname] - Converts Google urls (google.com/url) to normal urls
|
||||
where possible, in the specified nickname's last message. If nickname isn't provided,
|
||||
action will be performed on user's last message"""
|
||||
if not inp:
|
||||
inp = nick
|
||||
last_message = db.execute("select name, quote from seen_user where name"
|
||||
" like ? and chan = ?", (inp.lower(), input.chan.lower())).fetchone()
|
||||
if last_message:
|
||||
msg = last_message[1]
|
||||
out = ", ".join([(unquote(a[4:]) if a[:4] == "url=" else "") for a in msg.split("&")])\
|
||||
.replace(", ,", "").strip()
|
||||
return out if out else "No matches in your last message."
|
||||
else:
|
||||
if inp == nick:
|
||||
return "You haven't said anything in this channel yet!"
|
||||
else:
|
||||
return "That user hasn't said anything in this channel yet!"
|
|
@ -0,0 +1,89 @@
|
|||
from collections import deque
|
||||
from util import hook, timesince
|
||||
import time
|
||||
import re
|
||||
|
||||
db_ready = []
|
||||
|
||||
|
||||
def db_init(db, conn_name):
|
||||
"""check to see that our db has the the seen table (connection name is for caching the result per connection)"""
|
||||
global db_ready
|
||||
if db_ready.count(conn_name) < 1:
|
||||
db.execute("create table if not exists seen_user(name, time, quote, chan, host, "
|
||||
"primary key(name, chan))")
|
||||
db.commit()
|
||||
db_ready.append(conn_name)
|
||||
|
||||
|
||||
def track_seen(input, message_time, db, conn):
|
||||
""" Tracks messages for the .seen command """
|
||||
db_init(db, conn)
|
||||
# keep private messages private
|
||||
if input.chan[:1] == "#" and not re.findall('^s/.*/.*/$', input.msg.lower()):
|
||||
db.execute("insert or replace into seen_user(name, time, quote, chan, host)"
|
||||
"values(?,?,?,?,?)", (input.nick.lower(), message_time, input.msg,
|
||||
input.chan, input.mask))
|
||||
db.commit()
|
||||
|
||||
|
||||
def track_history(input, message_time, conn):
|
||||
try:
|
||||
history = conn.history[input.chan]
|
||||
except KeyError:
|
||||
conn.history[input.chan] = deque(maxlen=100)
|
||||
history = conn.history[input.chan]
|
||||
|
||||
data = (input.nick, message_time, input.msg)
|
||||
history.append(data)
|
||||
|
||||
|
||||
@hook.singlethread
|
||||
@hook.event('PRIVMSG', ignorebots=False)
|
||||
def chat_tracker(paraml, input=None, db=None, conn=None):
|
||||
message_time = time.time()
|
||||
track_seen(input, message_time, db, conn)
|
||||
track_history(input, message_time, conn)
|
||||
|
||||
|
||||
@hook.command(autohelp=False)
|
||||
def resethistory(inp, input=None, conn=None):
|
||||
"""resethistory - Resets chat history for the current channel"""
|
||||
try:
|
||||
conn.history[input.chan].clear()
|
||||
return "Reset chat history for current channel."
|
||||
except KeyError:
|
||||
# wat
|
||||
return "There is no history for this channel."
|
||||
|
||||
"""seen.py: written by sklnd in about two beers July 2009"""
|
||||
|
||||
@hook.command
|
||||
def seen(inp, nick='', chan='', db=None, input=None, conn=None):
|
||||
"""seen <nick> <channel> -- Tell when a nickname was last in active in one of this bot's channels."""
|
||||
|
||||
if input.conn.nick.lower() == inp.lower():
|
||||
return "You need to get your eyes checked."
|
||||
|
||||
if inp.lower() == nick.lower():
|
||||
return "Have you looked in a mirror lately?"
|
||||
|
||||
if not re.match("^[A-Za-z0-9_|.\-\]\[]*$", inp.lower()):
|
||||
return "I can't look up that name, its impossible to use!"
|
||||
|
||||
db_init(db, conn.name)
|
||||
|
||||
last_seen = db.execute("select name, time, quote from seen_user where name"
|
||||
" like ? and chan = ?", (inp, chan)).fetchone()
|
||||
|
||||
if last_seen:
|
||||
reltime = timesince.timesince(last_seen[1])
|
||||
if last_seen[0] != inp.lower(): # for glob matching
|
||||
inp = last_seen[0]
|
||||
if last_seen[2][0:1] == "\x01":
|
||||
return '{} was last seen {} ago: * {} {}'.format(inp, reltime, inp,
|
||||
last_seen[2][8:-1])
|
||||
else:
|
||||
return '{} was last seen {} ago saying: {}'.format(inp, reltime, last_seen[2])
|
||||
else:
|
||||
return "I've never seen {} talking in this channel.".format(inp)
|
|
@ -0,0 +1,56 @@
|
|||
# Plugin by Infinity - <https://github.com/infinitylabs/UguuBot>
|
||||
|
||||
from util import hook, http, text
|
||||
|
||||
db_ready = False
|
||||
|
||||
|
||||
def db_init(db):
|
||||
"""check to see that our db has the horoscope table and return a connection."""
|
||||
global db_ready
|
||||
if not db_ready:
|
||||
db.execute("create table if not exists horoscope(nick primary key, sign)")
|
||||
db.commit()
|
||||
db_ready = True
|
||||
|
||||
|
||||
@hook.command(autohelp=False)
|
||||
def horoscope(inp, db=None, notice=None, nick=None):
|
||||
"""horoscope <sign> -- Get your horoscope."""
|
||||
db_init(db)
|
||||
|
||||
# check if the user asked us not to save his details
|
||||
dontsave = inp.endswith(" dontsave")
|
||||
if dontsave:
|
||||
sign = inp[:-9].strip().lower()
|
||||
else:
|
||||
sign = inp
|
||||
|
||||
db.execute("create table if not exists horoscope(nick primary key, sign)")
|
||||
|
||||
if not sign:
|
||||
sign = db.execute("select sign from horoscope where nick=lower(?)",
|
||||
(nick,)).fetchone()
|
||||
if not sign:
|
||||
notice("horoscope <sign> -- Get your horoscope")
|
||||
return
|
||||
sign = sign[0]
|
||||
|
||||
url = "http://my.horoscope.com/astrology/free-daily-horoscope-{}.html".format(sign)
|
||||
soup = http.get_soup(url)
|
||||
|
||||
title = soup.find_all('h1', {'class': 'h1b'})[1]
|
||||
horoscope_text = soup.find('div', {'class': 'fontdef1'})
|
||||
result = u"\x02%s\x02 %s" % (title, horoscope_text)
|
||||
result = text.strip_html(result)
|
||||
#result = unicode(result, "utf8").replace('flight ','')
|
||||
|
||||
if not title:
|
||||
return "Could not get the horoscope for {}.".format(inp)
|
||||
|
||||
if inp and not dontsave:
|
||||
db.execute("insert or replace into horoscope(nick, sign) values (?,?)",
|
||||
(nick.lower(), sign))
|
||||
db.commit()
|
||||
|
||||
return result
|
|
@ -0,0 +1,30 @@
|
|||
from urllib import urlencode
|
||||
import re
|
||||
|
||||
from util import hook, http, timeformat
|
||||
|
||||
|
||||
hulu_re = (r'(.*://)(www.hulu.com|hulu.com)(.*)', re.I)
|
||||
|
||||
|
||||
@hook.regex(*hulu_re)
|
||||
def hulu_url(match):
|
||||
data = http.get_json("http://www.hulu.com/api/oembed.json?url=http://www.hulu.com" + match.group(3))
|
||||
showname = data['title'].split("(")[-1].split(")")[0]
|
||||
title = data['title'].split(" (")[0]
|
||||
return "{}: {} - {}".format(showname, title, timeformat.format_time(int(data['duration'])))
|
||||
|
||||
|
||||
@hook.command('hulu')
|
||||
def hulu_search(inp):
|
||||
"""hulu <search> - Search Hulu"""
|
||||
result = http.get_soup(
|
||||
"http://m.hulu.com/search?dp_identifier=hulu&{}&items_per_page=1&page=1".format(urlencode({'query': inp})))
|
||||
data = result.find('results').find('videos').find('video')
|
||||
showname = data.find('show').find('name').text
|
||||
title = data.find('title').text
|
||||
duration = timeformat.format_time(int(float(data.find('duration').text)))
|
||||
description = data.find('description').text
|
||||
rating = data.find('content-rating').text
|
||||
return "{}: {} - {} - {} ({}) {}".format(showname, title, description, duration, rating,
|
||||
"http://www.hulu.com/watch/" + str(data.find('id').text))
|
|
@ -0,0 +1,59 @@
|
|||
# IMDb lookup plugin by Ghetto Wizard (2011) and blha303 (2013)
|
||||
|
||||
import re
|
||||
|
||||
from util import hook, http, text
|
||||
|
||||
|
||||
id_re = re.compile("tt\d+")
|
||||
imdb_re = (r'(.*:)//(imdb.com|www.imdb.com)(:[0-9]+)?(.*)', re.I)
|
||||
|
||||
|
||||
@hook.command
|
||||
def imdb(inp):
|
||||
"""imdb <movie> -- Gets information about <movie> from IMDb."""
|
||||
|
||||
strip = inp.strip()
|
||||
|
||||
if id_re.match(strip):
|
||||
content = http.get_json("http://www.omdbapi.com/", i=strip)
|
||||
else:
|
||||
content = http.get_json("http://www.omdbapi.com/", t=strip)
|
||||
|
||||
if content.get('Error', None) == 'Movie not found!':
|
||||
return 'Movie not found!'
|
||||
elif content['Response'] == 'True':
|
||||
content['URL'] = 'http://www.imdb.com/title/{}'.format(content['imdbID'])
|
||||
|
||||
out = '\x02%(Title)s\x02 (%(Year)s) (%(Genre)s): %(Plot)s'
|
||||
if content['Runtime'] != 'N/A':
|
||||
out += ' \x02%(Runtime)s\x02.'
|
||||
if content['imdbRating'] != 'N/A' and content['imdbVotes'] != 'N/A':
|
||||
out += ' \x02%(imdbRating)s/10\x02 with \x02%(imdbVotes)s\x02' \
|
||||
' votes.'
|
||||
out += ' %(URL)s'
|
||||
return out % content
|
||||
else:
|
||||
return 'Unknown error.'
|
||||
|
||||
|
||||
@hook.regex(*imdb_re)
|
||||
def imdb_url(match):
|
||||
imdb_id = match.group(4).split('/')[-1]
|
||||
if imdb_id == "":
|
||||
imdb_id = match.group(4).split('/')[-2]
|
||||
content = http.get_json("http://www.omdbapi.com/", i=imdb_id)
|
||||
if content.get('Error', None) == 'Movie not found!':
|
||||
return 'Movie not found!'
|
||||
elif content['Response'] == 'True':
|
||||
content['URL'] = 'http://www.imdb.com/title/%(imdbID)s' % content
|
||||
content['Plot'] = text.truncate_str(content['Plot'], 50)
|
||||
out = '\x02%(Title)s\x02 (%(Year)s) (%(Genre)s): %(Plot)s'
|
||||
if content['Runtime'] != 'N/A':
|
||||
out += ' \x02%(Runtime)s\x02.'
|
||||
if content['imdbRating'] != 'N/A' and content['imdbVotes'] != 'N/A':
|
||||
out += ' \x02%(imdbRating)s/10\x02 with \x02%(imdbVotes)s\x02' \
|
||||
' votes.'
|
||||
return out % content
|
||||
else:
|
||||
return 'Unknown error.'
|
|
@ -0,0 +1,82 @@
|
|||
import re
|
||||
import random
|
||||
|
||||
from util import hook, http, web
|
||||
|
||||
|
||||
base_url = "http://reddit.com/r/{}/.json"
|
||||
imgur_re = re.compile(r'http://(?:i\.)?imgur\.com/(a/)?(\w+\b(?!/))\.?\w?')
|
||||
|
||||
album_api = "https://api.imgur.com/3/album/{}/images.json"
|
||||
|
||||
|
||||
def is_valid(data):
|
||||
if data["domain"] in ["i.imgur.com", "imgur.com"]:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@hook.command(autohelp=False)
|
||||
def imgur(inp):
|
||||
"""imgur [subreddit] -- Gets the first page of imgur images from [subreddit] and returns a link to them.
|
||||
If [subreddit] is undefined, return any imgur images"""
|
||||
if inp:
|
||||
# see if the input ends with "nsfw"
|
||||
show_nsfw = inp.endswith(" nsfw")
|
||||
|
||||
# remove "nsfw" from the input string after checking for it
|
||||
if show_nsfw:
|
||||
inp = inp[:-5].strip().lower()
|
||||
|
||||
url = base_url.format(inp.strip())
|
||||
else:
|
||||
url = "http://www.reddit.com/domain/imgur.com/.json"
|
||||
show_nsfw = False
|
||||
|
||||
try:
|
||||
data = http.get_json(url, user_agent=http.ua_chrome)
|
||||
except Exception as e:
|
||||
return "Error: " + str(e)
|
||||
|
||||
data = data["data"]["children"]
|
||||
random.shuffle(data)
|
||||
|
||||
# filter list to only have imgur links
|
||||
filtered_posts = [i["data"] for i in data if is_valid(i["data"])]
|
||||
|
||||
if not filtered_posts:
|
||||
return "No images found."
|
||||
|
||||
items = []
|
||||
|
||||
headers = {
|
||||
"Authorization": "Client-ID b5d127e6941b07a"
|
||||
}
|
||||
|
||||
# loop over the list of posts
|
||||
for post in filtered_posts:
|
||||
if post["over_18"] and not show_nsfw:
|
||||
continue
|
||||
|
||||
match = imgur_re.search(post["url"])
|
||||
if match.group(1) == 'a/':
|
||||
# post is an album
|
||||
url = album_api.format(match.group(2))
|
||||
images = http.get_json(url, headers=headers)["data"]
|
||||
|
||||
# loop over the images in the album and add to the list
|
||||
for image in images:
|
||||
items.append(image["id"])
|
||||
|
||||
elif match.group(2) is not None:
|
||||
# post is an image
|
||||
items.append(match.group(2))
|
||||
|
||||
if not items:
|
||||
return "No images found (use .imgur <subreddit> nsfw to show explicit content)"
|
||||
|
||||
if show_nsfw:
|
||||
return "{} \x02NSFW\x02".format(web.isgd("http://imgur.com/" + ','.join(items)))
|
||||
else:
|
||||
return web.isgd("http://imgur.com/" + ','.join(items))
|
|
@ -0,0 +1,28 @@
|
|||
import urlparse
|
||||
|
||||
from util import hook, http, urlnorm
|
||||
|
||||
|
||||
@hook.command
|
||||
def isup(inp):
|
||||
"""isup -- uses isup.me to see if a site is up or not"""
|
||||
|
||||
# slightly overcomplicated, esoteric URL parsing
|
||||
scheme, auth, path, query, fragment = urlparse.urlsplit(inp.strip())
|
||||
|
||||
domain = auth.encode('utf-8') or path.encode('utf-8')
|
||||
url = urlnorm.normalize(domain, assume_scheme="http")
|
||||
|
||||
try:
|
||||
soup = http.get_soup('http://isup.me/' + domain)
|
||||
except http.HTTPError, http.URLError:
|
||||
return "Could not get status."
|
||||
|
||||
content = soup.find('div').text.strip()
|
||||
|
||||
if "not just you" in content:
|
||||
return "It's not just you. {} looks \x02\x034down\x02\x0f from here!".format(url)
|
||||
elif "is up" in content:
|
||||
return "It's just you. {} is \x02\x033up\x02\x0f.".format(url)
|
||||
else:
|
||||
return "Huh? That doesn't look like a site on the interweb."
|
|
@ -0,0 +1,15 @@
|
|||
import re
|
||||
|
||||
from util import hook, http
|
||||
|
||||
|
||||
@hook.command(autohelp=False)
|
||||
def kernel(inp, reply=None):
|
||||
contents = http.get("https://www.kernel.org/finger_banner")
|
||||
contents = re.sub(r'The latest(\s*)', '', contents)
|
||||
contents = re.sub(r'version of the Linux kernel is:(\s*)', '- ', contents)
|
||||
lines = contents.split("\n")
|
||||
|
||||
message = "Linux kernel versions: "
|
||||
message += ", ".join(line for line in lines[:-1])
|
||||
reply(message)
|
|
@ -0,0 +1,33 @@
|
|||
import json
|
||||
|
||||
from util import hook, textgen
|
||||
|
||||
|
||||
def get_generator(_json, variables):
|
||||
data = json.loads(_json)
|
||||
return textgen.TextGenerator(data["templates"],
|
||||
data["parts"], variables=variables)
|
||||
|
||||
|
||||
@hook.command
|
||||
def kill(inp, action=None, nick=None, conn=None, notice=None):
|
||||
"""kill <user> -- Makes the bot kill <user>."""
|
||||
target = inp.strip()
|
||||
|
||||
if " " in target:
|
||||
notice("Invalid username!")
|
||||
return
|
||||
|
||||
# if the user is trying to make the bot kill itself, kill them
|
||||
if target.lower() == conn.nick.lower() or target.lower() == "itself":
|
||||
target = nick
|
||||
|
||||
variables = {
|
||||
"user": target
|
||||
}
|
||||
|
||||
with open("plugins/data/kills.json") as f:
|
||||
generator = get_generator(f.read(), variables)
|
||||
|
||||
# act out the message
|
||||
action(generator.generate_string())
|
|
@ -1,14 +1,16 @@
|
|||
from util import hook, http, timesince
|
||||
from datetime import datetime
|
||||
|
||||
from util import hook, http, timesince
|
||||
|
||||
|
||||
api_url = "http://ws.audioscrobbler.com/2.0/?format=json"
|
||||
|
||||
|
||||
@hook.command('l', autohelp=False)
|
||||
@hook.command(autohelp=False)
|
||||
def lastfm(inp, nick='', say=None, db=None, bot=None, notice=None):
|
||||
"lastfm [user] [dontsave] -- Displays the now playing (or last played)" \
|
||||
" track of LastFM user [user]."
|
||||
def lastfm(inp, nick='', db=None, bot=None, notice=None):
|
||||
"""lastfm [user] [dontsave] -- Displays the now playing (or last played)
|
||||
track of LastFM user [user]."""
|
||||
api_key = bot.config.get("api_keys", {}).get("lastfm")
|
||||
if not api_key:
|
||||
return "error: no api key set"
|
||||
|
@ -34,10 +36,10 @@ def lastfm(inp, nick='', say=None, db=None, bot=None, notice=None):
|
|||
api_key=api_key, user=user, limit=1)
|
||||
|
||||
if 'error' in response:
|
||||
return "Error: %s." % response["message"]
|
||||
return u"Error: {}.".format(response["message"])
|
||||
|
||||
if not "track" in response["recenttracks"] or len(response["recenttracks"]["track"]) == 0:
|
||||
return 'No recent tracks for user "%s" found.' % user
|
||||
return u'No recent tracks for user "{}" found.'.format(user)
|
||||
|
||||
tracks = response["recenttracks"]["track"]
|
||||
|
||||
|
@ -55,7 +57,7 @@ def lastfm(inp, nick='', say=None, db=None, bot=None, notice=None):
|
|||
# lets see how long ago they listened to it
|
||||
time_listened = datetime.fromtimestamp(int(track["date"]["uts"]))
|
||||
time_since = timesince.timesince(time_listened)
|
||||
ending = ' (%s ago)' % time_since
|
||||
ending = ' ({} ago)'.format(time_since)
|
||||
|
||||
else:
|
||||
return "error: could not parse track listing"
|
||||
|
@ -64,18 +66,18 @@ def lastfm(inp, nick='', say=None, db=None, bot=None, notice=None):
|
|||
album = track["album"]["#text"]
|
||||
artist = track["artist"]["#text"]
|
||||
|
||||
out = '%s %s "%s"' % (user, status, title)
|
||||
out = u'{} {} "{}"'.format(user, status, title)
|
||||
if artist:
|
||||
out += " by \x02%s\x0f" % artist
|
||||
out += u" by \x02{}\x0f".format(artist)
|
||||
if album:
|
||||
out += " from the album \x02%s\x0f" % album
|
||||
out += u" from the album \x02{}\x0f".format(album)
|
||||
|
||||
# append ending based on what type it was
|
||||
out += ending
|
||||
|
||||
if inp and not dontsave:
|
||||
db.execute("insert or replace into lastfm(nick, acc) values (?,?)",
|
||||
(nick.lower(), user))
|
||||
(nick.lower(), user))
|
||||
db.commit()
|
||||
|
||||
return out
|
|
@ -3,10 +3,10 @@ from util import hook, web, http
|
|||
|
||||
@hook.command('gfy')
|
||||
@hook.command
|
||||
def lmgtfy(inp, bot=None):
|
||||
"lmgtfy [phrase] - Posts a google link for the specified phrase"
|
||||
def lmgtfy(inp):
|
||||
"""lmgtfy [phrase] - Posts a google link for the specified phrase"""
|
||||
|
||||
link = "http://lmgtfy.com/?q=%s" % http.quote_plus(inp)
|
||||
link = u"http://lmgtfy.com/?q={}".format(http.quote_plus(inp))
|
||||
|
||||
try:
|
||||
return web.isgd(link)
|
|
@ -27,11 +27,11 @@ formats = {
|
|||
}
|
||||
|
||||
ctcp_formats = {
|
||||
'ACTION': '* %(nick)s %(ctcpmsg)s',
|
||||
'VERSION': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s',
|
||||
'PING': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s',
|
||||
'TIME': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s',
|
||||
'FINGER': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s'
|
||||
'ACTION': '* %(nick)s %(ctcpmsg)s',
|
||||
'VERSION': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s',
|
||||
'PING': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s',
|
||||
'TIME': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s',
|
||||
'FINGER': '%(nick)s has requested CTCP %(ctcpcmd)s from %(chan)s: %(ctcpmsg)s'
|
||||
}
|
||||
|
||||
irc_color_re = re.compile(r'(\x03(\d+,\d+|\d)|[\x0f\x02\x16\x1f])')
|
||||
|
@ -39,7 +39,7 @@ irc_color_re = re.compile(r'(\x03(\d+,\d+|\d)|[\x0f\x02\x16\x1f])')
|
|||
|
||||
def get_log_filename(dir, server, chan):
|
||||
return os.path.join(dir, 'log', gmtime('%Y'), server, chan,
|
||||
(gmtime('%%s.%m-%d.log') % chan).lower())
|
||||
(gmtime('%%s.%m-%d.log') % chan).lower())
|
||||
|
||||
|
||||
def gmtime(format):
|
||||
|
@ -64,8 +64,8 @@ def beautify(input):
|
|||
ctcp += ['']
|
||||
args['ctcpcmd'], args['ctcpmsg'] = ctcp
|
||||
format = ctcp_formats.get(args['ctcpcmd'],
|
||||
'%(nick)s [%(user)s@%(host)s] requested unknown CTCP '
|
||||
'%(ctcpcmd)s from %(chan)s: %(ctcpmsg)s')
|
||||
'%(nick)s [%(user)s@%(host)s] requested unknown CTCP '
|
||||
'%(ctcpcmd)s from %(chan)s: %(ctcpmsg)s')
|
||||
|
||||
return format % args
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
from util import hook, http, web
|
||||
|
||||
url = "http://search.azlyrics.com/search.php?q="
|
||||
|
||||
|
||||
@hook.command
|
||||
def lyrics(inp):
|
||||
"""lyrics <search> - Search AZLyrics.com for song lyrics"""
|
||||
if "pastelyrics" in inp:
|
||||
dopaste = True
|
||||
inp = inp.replace("pastelyrics", "").strip()
|
||||
else:
|
||||
dopaste = False
|
||||
soup = http.get_soup(url + inp.replace(" ", "+"))
|
||||
if "Try to compose less restrictive search query" in soup.find('div', {'id': 'inn'}).text:
|
||||
return "No results. Check spelling."
|
||||
div = None
|
||||
for i in soup.findAll('div', {'class': 'sen'}):
|
||||
if "/lyrics/" in i.find('a')['href']:
|
||||
div = i
|
||||
break
|
||||
if div:
|
||||
title = div.find('a').text
|
||||
link = div.find('a')['href']
|
||||
if dopaste:
|
||||
newsoup = http.get_soup(link)
|
||||
try:
|
||||
lyrics = newsoup.find('div', {'style': 'margin-left:10px;margin-right:10px;'}).text.strip()
|
||||
pasteurl = " " + web.haste(lyrics)
|
||||
except Exception as e:
|
||||
pasteurl = " (\x02Unable to paste lyrics\x02 [{}])".format(str(e))
|
||||
else:
|
||||
pasteurl = ""
|
||||
artist = div.find('b').text.title()
|
||||
lyricsum = div.find('div').text
|
||||
if "\r\n" in lyricsum.strip():
|
||||
lyricsum = " / ".join(lyricsum.strip().split("\r\n")[0:4]) # truncate, format
|
||||
else:
|
||||
lyricsum = " / ".join(lyricsum.strip().split("\n")[0:4]) # truncate, format
|
||||
return "\x02{}\x02 by \x02{}\x02 {}{} - {}".format(title, artist, web.try_isgd(link), pasteurl,
|
||||
lyricsum[:-3])
|
||||
else:
|
||||
return "No song results. " + url + inp.replace(" ", "+")
|
|
@ -9,14 +9,13 @@ from util import hook, http
|
|||
@hook.command('mc')
|
||||
@hook.command
|
||||
def metacritic(inp):
|
||||
"mc [all|movie|tv|album|x360|ps3|wii|pc|ds|3ds|vita] <title> -- Gets rating for <title> from metacritic on the specified medium."
|
||||
|
||||
# if the results suck, it's metacritic's fault
|
||||
"""mc [all|movie|tv|album|x360|ps3|pc|gba|ds|3ds|wii|vita|wiiu|xone|ps4] <title>
|
||||
Gets rating for <title> from metacritic on the specified medium."""
|
||||
|
||||
args = inp.strip()
|
||||
|
||||
game_platforms = ('x360', 'ps3', 'pc', 'ds', 'wii', '3ds', 'gba',
|
||||
'psp', 'vita')
|
||||
game_platforms = ('x360', 'ps3', 'pc', 'gba', 'ds', '3ds', 'wii',
|
||||
'vita', 'wiiu', 'xone', 'ps4')
|
||||
|
||||
all_platforms = game_platforms + ('all', 'movie', 'tv', 'album')
|
||||
|
||||
|
@ -35,45 +34,14 @@ def metacritic(inp):
|
|||
|
||||
title_safe = http.quote_plus(title)
|
||||
|
||||
url = 'http://www.metacritic.com/search/%s/%s/results' % (cat, title_safe)
|
||||
url = 'http://www.metacritic.com/search/{}/{}/results'.format(cat, title_safe)
|
||||
|
||||
try:
|
||||
doc = http.get_html(url)
|
||||
except HTTPError:
|
||||
return 'error fetching results'
|
||||
|
||||
''' result format:
|
||||
-- game result, with score
|
||||
-- subsequent results are the same structure, without first_result class
|
||||
<li class="result first_result">
|
||||
<div class="result_type">
|
||||
<strong>Game</strong>
|
||||
<span class="platform">WII</span>
|
||||
</div>
|
||||
<div class="result_wrap">
|
||||
<div class="basic_stats has_score">
|
||||
<div class="main_stats">
|
||||
<h3 class="product_title basic_stat">...</h3>
|
||||
<div class="std_score">
|
||||
<div class="score_wrap">
|
||||
<span class="label">Metascore: </span>
|
||||
<span class="data metascore score_favorable">87</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="more_stats extended_stats">...</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
-- other platforms are the same basic layout
|
||||
-- if it doesn't have a score, there is no div.basic_score
|
||||
-- the <div class="result_type"> changes content for non-games:
|
||||
<div class="result_type"><strong>Movie</strong></div>
|
||||
'''
|
||||
|
||||
# get the proper result element we want to pull data from
|
||||
|
||||
result = None
|
||||
|
||||
if not doc.find_class('query_results'):
|
||||
|
@ -118,7 +86,7 @@ def metacritic(inp):
|
|||
link = 'http://metacritic.com' + product_title.find('a').attrib['href']
|
||||
|
||||
try:
|
||||
release = result.find_class('release_date')[0].\
|
||||
release = result.find_class('release_date')[0]. \
|
||||
find_class('data')[0].text_content()
|
||||
|
||||
# strip extra spaces out of the release date
|
||||
|
@ -127,11 +95,10 @@ def metacritic(inp):
|
|||
release = None
|
||||
|
||||
try:
|
||||
score = result.find_class('metascore')[0].text_content()
|
||||
score = result.find_class('metascore_w')[0].text_content()
|
||||
except IndexError:
|
||||
score = None
|
||||
|
||||
return '[%s] %s - \x02%s/100\x02, %s - %s' % (plat.upper(), name,
|
||||
score or 'no score',
|
||||
'release: \x02%s\x02' % release if release else 'unreleased',
|
||||
link)
|
||||
return '[{}] {} - \x02{}/100\x02, {} - {}'.format(plat.upper(), name, score or 'no score',
|
||||
'release: \x02%s\x02' % release if release else 'unreleased',
|
||||
link)
|
|
@ -0,0 +1,154 @@
|
|||
import time
|
||||
import random
|
||||
|
||||
from util import hook, http, web, text
|
||||
|
||||
|
||||
## CONSTANTS
|
||||
|
||||
base_url = "http://api.bukget.org/3/"
|
||||
|
||||
search_url = base_url + "search/plugin_name/like/{}"
|
||||
random_url = base_url + "plugins/bukkit/?start={}&size=1"
|
||||
details_url = base_url + "plugins/bukkit/{}"
|
||||
|
||||
categories = http.get_json("http://api.bukget.org/3/categories")
|
||||
|
||||
count_total = sum([cat["count"] for cat in categories])
|
||||
count_categories = {cat["name"].lower(): int(cat["count"]) for cat in categories} # dict comps!
|
||||
|
||||
|
||||
class BukgetError(Exception):
|
||||
def __init__(self, code, text):
|
||||
self.code = code
|
||||
self.text = text
|
||||
|
||||
def __str__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
## DATA FUNCTIONS
|
||||
|
||||
def plugin_search(term):
|
||||
""" searches for a plugin with the bukget API and returns the slug """
|
||||
term = term.lower().strip()
|
||||
|
||||
search_term = http.quote_plus(term)
|
||||
|
||||
try:
|
||||
results = http.get_json(search_url.format(search_term))
|
||||
except (http.HTTPError, http.URLError) as e:
|
||||
raise BukgetError(500, "Error Fetching Search Page: {}".format(e))
|
||||
|
||||
if not results:
|
||||
raise BukgetError(404, "No Results Found")
|
||||
|
||||
for result in results:
|
||||
if result["slug"] == term:
|
||||
return result["slug"]
|
||||
|
||||
return results[0]["slug"]
|
||||
|
||||
|
||||
def plugin_random():
|
||||
""" gets a random plugin from the bukget API and returns the slug """
|
||||
results = None
|
||||
|
||||
while not results:
|
||||
plugin_number = random.randint(1, count_total)
|
||||
print "trying {}".format(plugin_number)
|
||||
try:
|
||||
results = http.get_json(random_url.format(plugin_number))
|
||||
except (http.HTTPError, http.URLError) as e:
|
||||
raise BukgetError(500, "Error Fetching Search Page: {}".format(e))
|
||||
|
||||
return results[0]["slug"]
|
||||
|
||||
|
||||
def plugin_details(slug):
|
||||
""" takes a plugin slug and returns details from the bukget API """
|
||||
slug = slug.lower().strip()
|
||||
|
||||
try:
|
||||
details = http.get_json(details_url.format(slug))
|
||||
except (http.HTTPError, http.URLError) as e:
|
||||
raise BukgetError(500, "Error Fetching Details: {}".format(e))
|
||||
return details
|
||||
|
||||
|
||||
## OTHER FUNCTIONS
|
||||
|
||||
def format_output(data):
|
||||
""" takes plugin data and returns two strings representing information about that plugin """
|
||||
name = data["plugin_name"]
|
||||
description = text.truncate_str(data['description'], 30)
|
||||
url = data['website']
|
||||
authors = data['authors'][0]
|
||||
authors = authors[0] + u"\u200b" + authors[1:]
|
||||
stage = data['stage']
|
||||
|
||||
current_version = data['versions'][0]
|
||||
|
||||
last_update = time.strftime('%d %B %Y %H:%M',
|
||||
time.gmtime(current_version['date']))
|
||||
version_number = data['versions'][0]['version']
|
||||
|
||||
bukkit_versions = ", ".join(current_version['game_versions'])
|
||||
link = web.try_isgd(current_version['link'])
|
||||
|
||||
if description:
|
||||
line_a = u"\x02{}\x02, by \x02{}\x02 - {} - ({}) \x02{}".format(name, authors, description, stage, url)
|
||||
else:
|
||||
line_a = u"\x02{}\x02, by \x02{}\x02 ({}) \x02{}".format(name, authors, stage, url)
|
||||
|
||||
line_b = u"Last release: \x02v{}\x02 for \x02{}\x02 at {} \x02{}\x02".format(version_number, bukkit_versions,
|
||||
last_update, link)
|
||||
|
||||
return line_a, line_b
|
||||
|
||||
|
||||
## HOOK FUNCTIONS
|
||||
|
||||
@hook.command('plugin')
|
||||
@hook.command
|
||||
def bukget(inp, reply=None, message=None):
|
||||
"""bukget <slug/name> - Look up a plugin on dev.bukkit.org"""
|
||||
# get the plugin slug using search
|
||||
try:
|
||||
slug = plugin_search(inp)
|
||||
except BukgetError as e:
|
||||
return e
|
||||
|
||||
# get the plugin info using the slug
|
||||
try:
|
||||
data = plugin_details(slug)
|
||||
except BukgetError as e:
|
||||
return e
|
||||
|
||||
# format the final message and send it to IRC
|
||||
line_a, line_b = format_output(data)
|
||||
|
||||
reply(line_a)
|
||||
message(line_b)
|
||||
|
||||
|
||||
@hook.command(autohelp=None)
|
||||
def randomplugin(inp, reply=None, message=None):
|
||||
"""randomplugin - Gets a random plugin from dev.bukkit.org"""
|
||||
# get a random plugin slug
|
||||
try:
|
||||
slug = plugin_random()
|
||||
except BukgetError as e:
|
||||
return e
|
||||
|
||||
# get the plugin info using the slug
|
||||
try:
|
||||
data = plugin_details(slug)
|
||||
except BukgetError as e:
|
||||
return e
|
||||
|
||||
# format the final message and send it to IRC
|
||||
line_a, line_b = format_output(data)
|
||||
|
||||
reply(line_a)
|
||||
message(line_b)
|
|
@ -1,9 +1,10 @@
|
|||
""" plugin by _303 (?)
|
||||
"""
|
||||
|
||||
from util import hook
|
||||
import re
|
||||
import itertools
|
||||
|
||||
from util import hook
|
||||
|
||||
|
||||
pattern = re.compile(r'^(?P<count>\d+)x (?P<name>.+?): (?P<ingredients>.*)$')
|
||||
|
||||
|
@ -32,9 +33,9 @@ with open("plugins/data/recipes.txt") as f:
|
|||
if not match:
|
||||
continue
|
||||
recipelist.append(Recipe(line=line,
|
||||
output=match.group("name").lower(),
|
||||
ingredients=match.group("ingredients"),
|
||||
count=match.group("count")))
|
||||
output=match.group("name").lower(),
|
||||
ingredients=match.group("ingredients"),
|
||||
count=match.group("count")))
|
||||
|
||||
ids = []
|
||||
|
||||
|
@ -43,36 +44,35 @@ with open("plugins/data/itemids.txt") as f:
|
|||
if line.startswith("//"):
|
||||
continue
|
||||
parts = line.strip().split()
|
||||
id = parts[0]
|
||||
itemid = parts[0]
|
||||
name = " ".join(parts[1:])
|
||||
ids.append((id, name))
|
||||
ids.append((itemid, name))
|
||||
|
||||
|
||||
@hook.command("mcid")
|
||||
@hook.command
|
||||
def mcitem(input, reply=None):
|
||||
"mcitem <item/id> -- gets the id from an item or vice versa"
|
||||
input = input.lower().strip()
|
||||
def mcitem(inp, reply=None):
|
||||
"""mcitem <item/id> -- gets the id from an item or vice versa"""
|
||||
inp = inp.lower().strip()
|
||||
|
||||
if input == "":
|
||||
if inp == "":
|
||||
reply("error: no input.")
|
||||
return
|
||||
|
||||
results = []
|
||||
|
||||
for id, name in ids:
|
||||
if input == id:
|
||||
results = ["\x02[%s]\x02 %s" % (id, name)]
|
||||
for item_id, item_name in ids:
|
||||
if inp == item_id:
|
||||
results = ["\x02[{}]\x02 {}".format(item_id, item_name)]
|
||||
break
|
||||
elif input in name.lower():
|
||||
results.append("\x02[%s]\x02 %s" % (id, name))
|
||||
elif inp in item_name.lower():
|
||||
results.append("\x02[{}]\x02 {}".format(item_id, item_name))
|
||||
|
||||
if not results:
|
||||
return "No matches found."
|
||||
|
||||
if len(results) > 12:
|
||||
reply("There are too many options, please narrow your search. " \
|
||||
"(%s)" % len(results))
|
||||
reply("There are too many options, please narrow your search. ({})".format(str(len(results))))
|
||||
return
|
||||
|
||||
out = ", ".join(results)
|
||||
|
@ -82,19 +82,18 @@ def mcitem(input, reply=None):
|
|||
|
||||
@hook.command("mccraft")
|
||||
@hook.command
|
||||
def mcrecipe(input, reply=None):
|
||||
"mcrecipe <item> -- gets the crafting recipe for an item"
|
||||
input = input.lower().strip()
|
||||
def mcrecipe(inp, reply=None):
|
||||
"""mcrecipe <item> -- gets the crafting recipe for an item"""
|
||||
inp = inp.lower().strip()
|
||||
|
||||
results = [recipe.line for recipe in recipelist
|
||||
if input in recipe.output]
|
||||
if inp in recipe.output]
|
||||
|
||||
if not results:
|
||||
return "No matches found."
|
||||
|
||||
if len(results) > 3:
|
||||
reply("There are too many options, please narrow your search. " \
|
||||
"(%s)" % len(results))
|
||||
reply("There are too many options, please narrow your search. ({})".format(len(results)))
|
||||
return
|
||||
|
||||
for result in results:
|
|
@ -0,0 +1,232 @@
|
|||
import socket
|
||||
import struct
|
||||
import json
|
||||
import traceback
|
||||
|
||||
from util import hook
|
||||
|
||||
|
||||
try:
|
||||
import DNS
|
||||
has_dns = True
|
||||
except ImportError:
|
||||
has_dns = False
|
||||
|
||||
|
||||
mc_colors = [(u'\xa7f', u'\x0300'), (u'\xa70', u'\x0301'), (u'\xa71', u'\x0302'), (u'\xa72', u'\x0303'),
|
||||
(u'\xa7c', u'\x0304'), (u'\xa74', u'\x0305'), (u'\xa75', u'\x0306'), (u'\xa76', u'\x0307'),
|
||||
(u'\xa7e', u'\x0308'), (u'\xa7a', u'\x0309'), (u'\xa73', u'\x0310'), (u'\xa7b', u'\x0311'),
|
||||
(u'\xa71', u'\x0312'), (u'\xa7d', u'\x0313'), (u'\xa78', u'\x0314'), (u'\xa77', u'\x0315'),
|
||||
(u'\xa7l', u'\x02'), (u'\xa79', u'\x0310'), (u'\xa7o', u'\t'), (u'\xa7m', u'\x13'),
|
||||
(u'\xa7r', u'\x0f'), (u'\xa7n', u'\x15')]
|
||||
|
||||
|
||||
## EXCEPTIONS
|
||||
|
||||
|
||||
class PingError(Exception):
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
|
||||
def __str__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
class ParseError(Exception):
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
|
||||
def __str__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
## MISC
|
||||
|
||||
|
||||
def unpack_varint(s):
|
||||
d = 0
|
||||
i = 0
|
||||
while True:
|
||||
b = ord(s.recv(1))
|
||||
d |= (b & 0x7F) << 7 * i
|
||||
i += 1
|
||||
if not b & 0x80:
|
||||
return d
|
||||
|
||||
pack_data = lambda d: struct.pack('>b', len(d)) + d
|
||||
pack_port = lambda i: struct.pack('>H', i)
|
||||
|
||||
## DATA FUNCTIONS
|
||||
|
||||
|
||||
def mcping_modern(host, port):
|
||||
""" pings a server using the modern (1.7+) protocol and returns data """
|
||||
try:
|
||||
# connect to the server
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
try:
|
||||
s.connect((host, port))
|
||||
except socket.gaierror:
|
||||
raise PingError("Invalid hostname")
|
||||
except socket.timeout:
|
||||
raise PingError("Request timed out")
|
||||
|
||||
# send handshake + status request
|
||||
s.send(pack_data("\x00\x00" + pack_data(host.encode('utf8')) + pack_port(port) + "\x01"))
|
||||
s.send(pack_data("\x00"))
|
||||
|
||||
# read response
|
||||
unpack_varint(s) # Packet length
|
||||
unpack_varint(s) # Packet ID
|
||||
l = unpack_varint(s) # String length
|
||||
|
||||
if not l > 1:
|
||||
raise PingError("Invalid response")
|
||||
|
||||
d = ""
|
||||
while len(d) < l:
|
||||
d += s.recv(1024)
|
||||
|
||||
# Close our socket
|
||||
s.close()
|
||||
except socket.error:
|
||||
raise PingError("Socket Error")
|
||||
|
||||
# Load json and return
|
||||
data = json.loads(d.decode('utf8'))
|
||||
try:
|
||||
version = data["version"]["name"]
|
||||
try:
|
||||
desc = u" ".join(data["description"]["text"].split())
|
||||
except TypeError:
|
||||
desc = u" ".join(data["description"].split())
|
||||
max_players = data["players"]["max"]
|
||||
online = data["players"]["online"]
|
||||
except Exception as e:
|
||||
# TODO: except Exception is bad
|
||||
traceback.print_exc(e)
|
||||
raise PingError("Unknown Error: {}".format(e))
|
||||
|
||||
output = {
|
||||
"motd": format_colors(desc),
|
||||
"motd_raw": desc,
|
||||
"version": version,
|
||||
"players": online,
|
||||
"players_max": max_players
|
||||
}
|
||||
return output
|
||||
|
||||
|
||||
def mcping_legacy(host, port):
|
||||
""" pings a server using the legacy (1.6 and older) protocol and returns data """
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
try:
|
||||
sock.connect((host, port))
|
||||
sock.send('\xfe\x01')
|
||||
response = sock.recv(1)
|
||||
except socket.gaierror:
|
||||
raise PingError("Invalid hostname")
|
||||
except socket.timeout:
|
||||
raise PingError("Request timed out")
|
||||
|
||||
if response[0] != '\xff':
|
||||
raise PingError("Invalid response")
|
||||
|
||||
length = struct.unpack('!h', sock.recv(2))[0]
|
||||
values = sock.recv(length * 2).decode('utf-16be')
|
||||
data = values.split(u'\x00') # try to decode data using new format
|
||||
if len(data) == 1:
|
||||
# failed to decode data, server is using old format
|
||||
data = values.split(u'\xa7')
|
||||
output = {
|
||||
"motd": format_colors(" ".join(data[0].split())),
|
||||
"motd_raw": data[0],
|
||||
"version": None,
|
||||
"players": data[1],
|
||||
"players_max": data[2]
|
||||
}
|
||||
else:
|
||||
# decoded data, server is using new format
|
||||
output = {
|
||||
"motd": format_colors(" ".join(data[3].split())),
|
||||
"motd_raw": data[3],
|
||||
"version": data[2],
|
||||
"players": data[4],
|
||||
"players_max": data[5]
|
||||
}
|
||||
sock.close()
|
||||
return output
|
||||
|
||||
|
||||
## FORMATTING/PARSING FUNCTIONS
|
||||
|
||||
def check_srv(domain):
|
||||
""" takes a domain and finds minecraft SRV records """
|
||||
DNS.DiscoverNameServers()
|
||||
srv_req = DNS.Request(qtype='srv')
|
||||
srv_result = srv_req.req('_minecraft._tcp.{}'.format(domain))
|
||||
|
||||
for getsrv in srv_result.answers:
|
||||
if getsrv['typename'] == 'SRV':
|
||||
data = [getsrv['data'][2], getsrv['data'][3]]
|
||||
return data
|
||||
|
||||
|
||||
def parse_input(inp):
|
||||
""" takes the input from the mcping command and returns the host and port """
|
||||
inp = inp.strip().split(" ")[0]
|
||||
if ":" in inp:
|
||||
# the port is defined in the input string
|
||||
host, port = inp.split(":", 1)
|
||||
try:
|
||||
port = int(port)
|
||||
if port > 65535 or port < 0:
|
||||
raise ParseError("The port '{}' is invalid.".format(port))
|
||||
except ValueError:
|
||||
raise ParseError("The port '{}' is invalid.".format(port))
|
||||
return host, port
|
||||
if has_dns:
|
||||
# the port is not in the input string, but we have PyDNS so look for a SRV record
|
||||
srv_data = check_srv(inp)
|
||||
if srv_data:
|
||||
return str(srv_data[1]), int(srv_data[0])
|
||||
# return default port
|
||||
return inp, 25565
|
||||
|
||||
|
||||
def format_colors(motd):
|
||||
for original, replacement in mc_colors:
|
||||
motd = motd.replace(original, replacement)
|
||||
motd = motd.replace(u"\xa7k", "")
|
||||
return motd
|
||||
|
||||
|
||||
def format_output(data):
|
||||
if data["version"]:
|
||||
return u"{motd}\x0f - {version}\x0f - {players}/{players_max}" \
|
||||
u" players.".format(**data).replace("\n", u"\x0f - ")
|
||||
else:
|
||||
return u"{motd}\x0f - {players}/{players_max}" \
|
||||
u" players.".format(**data).replace("\n", u"\x0f - ")
|
||||
|
||||
|
||||
@hook.command
|
||||
@hook.command("mcp")
|
||||
def mcping(inp):
|
||||
"""mcping <server>[:port] - Ping a Minecraft server to check status."""
|
||||
try:
|
||||
host, port = parse_input(inp)
|
||||
except ParseError as e:
|
||||
return "Could not parse input ({})".format(e)
|
||||
|
||||
try:
|
||||
data = mcping_modern(host, port)
|
||||
except PingError:
|
||||
try:
|
||||
data = mcping_legacy(host, port)
|
||||
except PingError as e:
|
||||
return "Could not ping server, is it offline? ({})".format(e)
|
||||
|
||||
return format_output(data)
|
|
@ -0,0 +1,44 @@
|
|||
import json
|
||||
|
||||
from util import hook, http
|
||||
|
||||
|
||||
@hook.command(autohelp=False)
|
||||
def mcstatus(inp):
|
||||
"""mcstatus -- Checks the status of various Mojang (the creators of Minecraft) servers."""
|
||||
|
||||
try:
|
||||
request = http.get("http://status.mojang.com/check")
|
||||
except (http.URLError, http.HTTPError) as e:
|
||||
return "Unable to get Minecraft server status: {}".format(e)
|
||||
|
||||
# lets just reformat this data to get in a nice format
|
||||
data = json.loads(request.replace("}", "").replace("{", "").replace("]", "}").replace("[", "{"))
|
||||
|
||||
out = []
|
||||
|
||||
# use a loop so we don't have to update it if they add more servers
|
||||
green = []
|
||||
yellow = []
|
||||
red = []
|
||||
for server, status in data.items():
|
||||
if status == "green":
|
||||
green.append(server)
|
||||
elif status == "yellow":
|
||||
yellow.append(server)
|
||||
else:
|
||||
red.append(server)
|
||||
|
||||
if green:
|
||||
out = "\x033\x02Online\x02\x0f: " + ", ".join(green)
|
||||
if yellow:
|
||||
out += " "
|
||||
if yellow:
|
||||
out += "\x02Issues\x02: " + ", ".join(yellow)
|
||||
if red:
|
||||
out += " "
|
||||
if red:
|
||||
out += "\x034\x02Offline\x02\x0f: " + ", ".join(red)
|
||||
|
||||
return "\x0f" + out.replace(".mojang.com", ".mj") \
|
||||
.replace(".minecraft.net", ".mc")
|
|
@ -0,0 +1,101 @@
|
|||
import json
|
||||
from util import hook, http
|
||||
|
||||
NAME_URL = "https://account.minecraft.net/buy/frame/checkName/{}"
|
||||
PAID_URL = "http://www.minecraft.net/haspaid.jsp"
|
||||
|
||||
|
||||
class McuError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_status(name):
|
||||
""" takes a name and returns status """
|
||||
try:
|
||||
name_encoded = http.quote_plus(name)
|
||||
response = http.get(NAME_URL.format(name_encoded))
|
||||
except (http.URLError, http.HTTPError) as e:
|
||||
raise McuError("Could not get name status: {}".format(e))
|
||||
|
||||
if "OK" in response:
|
||||
return "free"
|
||||
elif "TAKEN" in response:
|
||||
return "taken"
|
||||
elif "invalid characters" in response:
|
||||
return "invalid"
|
||||
|
||||
|
||||
def get_profile(name):
|
||||
profile = {}
|
||||
|
||||
# form the profile request
|
||||
request = {
|
||||
"name": name,
|
||||
"agent": "minecraft"
|
||||
}
|
||||
|
||||
# submit the profile request
|
||||
try:
|
||||
headers = {"Content-Type": "application/json"}
|
||||
r = http.get_json(
|
||||
'https://api.mojang.com/profiles/page/1',
|
||||
post_data=json.dumps(request),
|
||||
headers=headers
|
||||
)
|
||||
except (http.URLError, http.HTTPError) as e:
|
||||
raise McuError("Could not get profile status: {}".format(e))
|
||||
|
||||
user = r["profiles"][0]
|
||||
profile["name"] = user["name"]
|
||||
profile["id"] = user["id"]
|
||||
|
||||
profile["legacy"] = user.get("legacy", False)
|
||||
|
||||
try:
|
||||
response = http.get(PAID_URL, user=name)
|
||||
except (http.URLError, http.HTTPError) as e:
|
||||
raise McuError("Could not get payment status: {}".format(e))
|
||||
|
||||
if "true" in response:
|
||||
profile["paid"] = True
|
||||
else:
|
||||
profile["paid"] = False
|
||||
|
||||
return profile
|
||||
|
||||
|
||||
@hook.command("haspaid")
|
||||
@hook.command("mcpaid")
|
||||
@hook.command
|
||||
def mcuser(inp):
|
||||
"""mcpaid <username> -- Gets information about the Minecraft user <account>."""
|
||||
user = inp.strip()
|
||||
|
||||
try:
|
||||
# get status of name (does it exist?)
|
||||
name_status = get_status(user)
|
||||
except McuError as e:
|
||||
return e
|
||||
|
||||
if name_status == "taken":
|
||||
try:
|
||||
# get information about user
|
||||
profile = get_profile(user)
|
||||
except McuError as e:
|
||||
return "Error: {}".format(e)
|
||||
|
||||
profile["lt"] = ", legacy" if profile["legacy"] else ""
|
||||
|
||||
if profile["paid"]:
|
||||
return u"The account \x02{name}\x02 ({id}{lt}) exists. It is a \x02paid\x02" \
|
||||
u" account.".format(**profile)
|
||||
else:
|
||||
return u"The account \x02{name}\x02 ({id}{lt}) exists. It \x034\x02is NOT\x02\x0f a paid" \
|
||||
u" account.".format(**profile)
|
||||
elif name_status == "free":
|
||||
return u"The account \x02{}\x02 does not exist.".format(user)
|
||||
elif name_status == "invalid":
|
||||
return u"The name \x02{}\x02 contains invalid characters.".format(user)
|
||||
else:
|
||||
# if you see this, panic
|
||||
return "Unknown Error."
|
|
@ -0,0 +1,51 @@
|
|||
import re
|
||||
|
||||
from util import hook, http, text
|
||||
|
||||
|
||||
api_url = "http://minecraft.gamepedia.com/api.php?action=opensearch"
|
||||
mc_url = "http://minecraft.gamepedia.com/"
|
||||
|
||||
|
||||
@hook.command
|
||||
def mcwiki(inp):
|
||||
"""mcwiki <phrase> -- Gets the first paragraph of
|
||||
the Minecraft Wiki article on <phrase>."""
|
||||
|
||||
try:
|
||||
j = http.get_json(api_url, search=inp)
|
||||
except (http.HTTPError, http.URLError) as e:
|
||||
return "Error fetching search results: {}".format(e)
|
||||
except ValueError as e:
|
||||
return "Error reading search results: {}".format(e)
|
||||
|
||||
if not j[1]:
|
||||
return "No results found."
|
||||
|
||||
# we remove items with a '/' in the name, because
|
||||
# gamepedia uses sub-pages for different languages
|
||||
# for some stupid reason
|
||||
items = [item for item in j[1] if not "/" in item]
|
||||
|
||||
if items:
|
||||
article_name = items[0].replace(' ', '_').encode('utf8')
|
||||
else:
|
||||
# there are no items without /, just return a / one
|
||||
article_name = j[1][0].replace(' ', '_').encode('utf8')
|
||||
|
||||
url = mc_url + http.quote(article_name, '')
|
||||
|
||||
try:
|
||||
page = http.get_html(url)
|
||||
except (http.HTTPError, http.URLError) as e:
|
||||
return "Error fetching wiki page: {}".format(e)
|
||||
|
||||
for p in page.xpath('//div[@class="mw-content-ltr"]/p'):
|
||||
if p.text_content():
|
||||
summary = " ".join(p.text_content().splitlines())
|
||||
summary = re.sub("\[\d+\]", "", summary)
|
||||
summary = text.truncate_str(summary, 200)
|
||||
return u"{} :: {}".format(summary, url)
|
||||
|
||||
# this shouldn't happen
|
||||
return "Unknown Error."
|
|
@ -0,0 +1,34 @@
|
|||
# Plugin by Infinity - <https://github.com/infinitylabs/UguuBot>
|
||||
|
||||
import random
|
||||
|
||||
from util import hook, http
|
||||
|
||||
|
||||
mlia_cache = []
|
||||
|
||||
|
||||
def refresh_cache():
|
||||
"""gets a page of random MLIAs and puts them into a dictionary """
|
||||
url = 'http://mylifeisaverage.com/{}'.format(random.randint(1, 11000))
|
||||
soup = http.get_soup(url)
|
||||
|
||||
for story in soup.find_all('div', {'class': 'story '}):
|
||||
mlia_id = story.find('span', {'class': 'left'}).a.text
|
||||
mlia_text = story.find('div', {'class': 'sc'}).text.strip()
|
||||
mlia_cache.append((mlia_id, mlia_text))
|
||||
|
||||
# do an initial refresh of the cache
|
||||
refresh_cache()
|
||||
|
||||
|
||||
@hook.command(autohelp=False)
|
||||
def mlia(inp, reply=None):
|
||||
"""mlia -- Gets a random quote from MyLifeIsAverage.com."""
|
||||
# grab the last item in the mlia cache and remove it
|
||||
mlia_id, text = mlia_cache.pop()
|
||||
# reply with the mlia we grabbed
|
||||
reply('({}) {}'.format(mlia_id, text))
|
||||
# refresh mlia cache if its getting empty
|
||||
if len(mlia_cache) < 3:
|
||||
refresh_cache()
|
2
disabled_plugins/mygengo_translate.py → disabled_stuff/mygengo_translate.py
Executable file → Normal file
2
disabled_plugins/mygengo_translate.py → disabled_stuff/mygengo_translate.py
Executable file → Normal file
|
@ -1,7 +1,7 @@
|
|||
# BING translation plugin by Lukeroge and neersighted
|
||||
from util import hook
|
||||
from util import http
|
||||
import re
|
||||
import re
|
||||
import htmlentitydefs
|
||||
import mygengo
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import json
|
||||
import os
|
||||
|
||||
from util import hook, text, textgen
|
||||
|
||||
|
||||
GEN_DIR = "./plugins/data/name_files/"
|
||||
|
||||
|
||||
def get_generator(_json):
|
||||
data = json.loads(_json)
|
||||
return textgen.TextGenerator(data["templates"],
|
||||
data["parts"], default_templates=data["default_templates"])
|
||||
|
||||
|
||||
@hook.command(autohelp=False)
|
||||
def namegen(inp, notice=None):
|
||||
"""namegen [generator] -- Generates some names using the chosen generator.
|
||||
'namegen list' will display a list of all generators."""
|
||||
|
||||
# clean up the input
|
||||
inp = inp.strip().lower()
|
||||
|
||||
# get a list of available name generators
|
||||
files = os.listdir(GEN_DIR)
|
||||
all_modules = []
|
||||
for i in files:
|
||||
if os.path.splitext(i)[1] == ".json":
|
||||
all_modules.append(os.path.splitext(i)[0])
|
||||
all_modules.sort()
|
||||
|
||||
# command to return a list of all available generators
|
||||
if inp == "list":
|
||||
message = "Available generators: "
|
||||
message += text.get_text_list(all_modules, 'and')
|
||||
notice(message)
|
||||
return
|
||||
|
||||
if inp:
|
||||
selected_module = inp.split()[0]
|
||||
else:
|
||||
# make some generic fantasy names
|
||||
selected_module = "fantasy"
|
||||
|
||||
# check if the selected module is valid
|
||||
if not selected_module in all_modules:
|
||||
return "Invalid name generator :("
|
||||
|
||||
# load the name generator
|
||||
with open(os.path.join(GEN_DIR, "{}.json".format(selected_module))) as f:
|
||||
try:
|
||||
generator = get_generator(f.read())
|
||||
except ValueError as error:
|
||||
return "Unable to read name file: {}".format(error)
|
||||
|
||||
# time to generate some names
|
||||
name_list = generator.generate_strings(10)
|
||||
|
||||
# and finally return the final message :D
|
||||
return "Some names to ponder: {}.".format(text.get_text_list(name_list, 'and'))
|
|
@ -0,0 +1,95 @@
|
|||
import json
|
||||
import re
|
||||
|
||||
from util import hook, http, text, web
|
||||
|
||||
|
||||
## CONSTANTS
|
||||
|
||||
ITEM_URL = "http://www.newegg.com/Product/Product.aspx?Item={}"
|
||||
|
||||
API_PRODUCT = "http://www.ows.newegg.com/Products.egg/{}/ProductDetails"
|
||||
API_SEARCH = "http://www.ows.newegg.com/Search.egg/Advanced"
|
||||
|
||||
NEWEGG_RE = (r"(?:(?:www.newegg.com|newegg.com)/Product/Product\.aspx\?Item=)([-_a-zA-Z0-9]+)", re.I)
|
||||
|
||||
|
||||
## OTHER FUNCTIONS
|
||||
|
||||
def format_item(item, show_url=True):
|
||||
""" takes a newegg API item object and returns a description """
|
||||
title = text.truncate_str(item["Title"], 50)
|
||||
|
||||
# format the rating nicely if it exists
|
||||
if not item["ReviewSummary"]["TotalReviews"] == "[]":
|
||||
rating = "Rated {}/5 ({} ratings)".format(item["ReviewSummary"]["Rating"],
|
||||
item["ReviewSummary"]["TotalReviews"][1:-1])
|
||||
else:
|
||||
rating = "No Ratings"
|
||||
|
||||
if not item["FinalPrice"] == item["OriginalPrice"]:
|
||||
price = "{FinalPrice}, was {OriginalPrice}".format(**item)
|
||||
else:
|
||||
price = item["FinalPrice"]
|
||||
|
||||
tags = []
|
||||
|
||||
if item["Instock"]:
|
||||
tags.append("\x02Stock Available\x02")
|
||||
else:
|
||||
tags.append("\x02Out Of Stock\x02")
|
||||
|
||||
if item["FreeShippingFlag"]:
|
||||
tags.append("\x02Free Shipping\x02")
|
||||
|
||||
if item["IsFeaturedItem"]:
|
||||
tags.append("\x02Featured\x02")
|
||||
|
||||
if item["IsShellShockerItem"]:
|
||||
tags.append(u"\x02SHELL SHOCKER\u00AE\x02")
|
||||
|
||||
# join all the tags together in a comma separated string ("tag1, tag2, tag3")
|
||||
tag_text = u", ".join(tags)
|
||||
|
||||
if show_url:
|
||||
# create the item URL and shorten it
|
||||
url = web.try_isgd(ITEM_URL.format(item["NeweggItemNumber"]))
|
||||
return u"\x02{}\x02 ({}) - {} - {} - {}".format(title, price, rating,
|
||||
tag_text, url)
|
||||
else:
|
||||
return u"\x02{}\x02 ({}) - {} - {}".format(title, price, rating,
|
||||
tag_text)
|
||||
|
||||
|
||||
## HOOK FUNCTIONS
|
||||
|
||||
@hook.regex(*NEWEGG_RE)
|
||||
def newegg_url(match):
|
||||
item_id = match.group(1)
|
||||
item = http.get_json(API_PRODUCT.format(item_id))
|
||||
return format_item(item, show_url=False)
|
||||
|
||||
|
||||
@hook.command
|
||||
def newegg(inp):
|
||||
"""newegg <item name> -- Searches newegg.com for <item name>"""
|
||||
|
||||
# form the search request
|
||||
request = {
|
||||
"Keyword": inp,
|
||||
"Sort": "FEATURED"
|
||||
}
|
||||
|
||||
# submit the search request
|
||||
r = http.get_json(
|
||||
'http://www.ows.newegg.com/Search.egg/Advanced',
|
||||
post_data=json.dumps(request)
|
||||
)
|
||||
|
||||
# get the first result
|
||||
if r["ProductListItems"]:
|
||||
return format_item(r["ProductListItems"][0])
|
||||
else:
|
||||
return "No results found."
|
||||
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import re
|
||||
|
||||
from util import hook, http
|
||||
|
||||
|
||||
newgrounds_re = (r'(.*:)//(www.newgrounds.com|newgrounds.com)(:[0-9]+)?(.*)', re.I)
|
||||
valid = set('0123456789')
|
||||
|
||||
|
||||
def test(s):
|
||||
return set(s) <= valid
|
||||
|
||||
|
||||
@hook.regex(*newgrounds_re)
|
||||
def newgrounds_url(match):
|
||||
location = match.group(4).split("/")[-1]
|
||||
if not test(location):
|
||||
print "Not a valid Newgrounds portal ID. Example: http://www.newgrounds.com/portal/view/593993"
|
||||
return None
|
||||
soup = http.get_soup("http://www.newgrounds.com/portal/view/" + location)
|
||||
|
||||
title = "\x02{}\x02".format(soup.find('title').text)
|
||||
|
||||
# get author
|
||||
try:
|
||||
author_info = soup.find('ul', {'class': 'authorlinks'}).find('img')['alt']
|
||||
author = " - \x02{}\x02".format(author_info)
|
||||
except:
|
||||
author = ""
|
||||
|
||||
# get rating
|
||||
try:
|
||||
rating_info = soup.find('dd', {'class': 'star-variable'})['title'].split("Stars –")[0].strip()
|
||||
rating = u" - rated \x02{}\x02/\x025.0\x02".format(rating_info)
|
||||
except:
|
||||
rating = ""
|
||||
|
||||
# get amount of ratings
|
||||
try:
|
||||
ratings_info = soup.find('dd', {'class': 'star-variable'})['title'].split("Stars –")[1].replace("Votes",
|
||||
"").strip()
|
||||
numofratings = " ({})".format(ratings_info)
|
||||
except:
|
||||
numofratings = ""
|
||||
|
||||
# get amount of views
|
||||
try:
|
||||
views_info = soup.find('dl', {'class': 'contentdata'}).findAll('dd')[1].find('strong').text
|
||||
views = " - \x02{}\x02 views".format(views_info)
|
||||
except:
|
||||
views = ""
|
||||
|
||||
# get upload data
|
||||
try:
|
||||
date = "on \x02{}\x02".format(soup.find('dl', {'class': 'sidestats'}).find('dd').text)
|
||||
except:
|
||||
date = ""
|
||||
|
||||
return title + rating + numofratings + views + author + date
|
|
@ -1,16 +1,18 @@
|
|||
from util import hook
|
||||
import re
|
||||
|
||||
db_inited = False
|
||||
from util import hook
|
||||
|
||||
|
||||
def cleanSQL(sql):
|
||||
db_ready = False
|
||||
|
||||
|
||||
def clean_sql(sql):
|
||||
return re.sub(r'\s+', " ", sql).strip()
|
||||
|
||||
|
||||
def db_init(db):
|
||||
global db_inited
|
||||
if db_inited:
|
||||
global db_ready
|
||||
if db_ready:
|
||||
return
|
||||
|
||||
exists = db.execute("""
|
||||
|
@ -20,7 +22,7 @@ def db_init(db):
|
|||
""").fetchone()[0] == 1
|
||||
|
||||
if not exists:
|
||||
db.execute(cleanSQL("""
|
||||
db.execute(clean_sql("""
|
||||
create virtual table todos using fts4(
|
||||
user,
|
||||
text,
|
||||
|
@ -30,7 +32,7 @@ def db_init(db):
|
|||
|
||||
db.commit()
|
||||
|
||||
db_inited = True
|
||||
db_ready = True
|
||||
|
||||
|
||||
def db_getall(db, nick, limit=-1):
|
||||
|
@ -44,14 +46,14 @@ def db_getall(db, nick, limit=-1):
|
|||
""", (nick, limit))
|
||||
|
||||
|
||||
def db_get(db, nick, id):
|
||||
def db_get(db, nick, note_id):
|
||||
return db.execute("""
|
||||
select added, text from todos
|
||||
where lower(user) = lower(?)
|
||||
order by added desc
|
||||
limit 1
|
||||
offset ?
|
||||
""", (nick, id)).fetchone()
|
||||
""", (nick, note_id)).fetchone()
|
||||
|
||||
|
||||
def db_del(db, nick, limit='all'):
|
||||
|
@ -64,8 +66,8 @@ def db_del(db, nick, limit='all'):
|
|||
limit ?
|
||||
offset ?)
|
||||
""", (nick,
|
||||
-1 if limit == 'all' else 1,
|
||||
0 if limit == 'all' else limit))
|
||||
-1 if limit == 'all' else 1,
|
||||
0 if limit == 'all' else limit))
|
||||
db.commit()
|
||||
return row
|
||||
|
||||
|
@ -88,9 +90,10 @@ def db_search(db, nick, query):
|
|||
""", (query, nick))
|
||||
|
||||
|
||||
@hook.command("notes")
|
||||
@hook.command
|
||||
def todo(inp, nick='', chan='', db=None, notice=None, bot=None):
|
||||
"todo (add|del|list|search) args -- Manipulates your list of todos."
|
||||
def note(inp, nick='', chan='', db=None, notice=None, bot=None):
|
||||
"""note(s) <add|del|list|search> args -- Manipulates your list of notes."""
|
||||
|
||||
db_init(db)
|
||||
|
||||
|
@ -100,7 +103,7 @@ def todo(inp, nick='', chan='', db=None, notice=None, bot=None):
|
|||
args = parts[1:]
|
||||
|
||||
# code to allow users to access each others factoids and a copy of help
|
||||
# ".todo (add|del|list|search) [@user] args -- Manipulates your list of todos."
|
||||
# ".note (add|del|list|search) [@user] args -- Manipulates your list of todos."
|
||||
#if len(args) and args[0].startswith("@"):
|
||||
# nick = args[0][1:]
|
||||
# args = args[1:]
|
||||
|
@ -113,7 +116,7 @@ def todo(inp, nick='', chan='', db=None, notice=None, bot=None):
|
|||
|
||||
db_add(db, nick, text)
|
||||
|
||||
notice("Task added!")
|
||||
notice("Note added!")
|
||||
return
|
||||
elif cmd == 'get':
|
||||
if len(args):
|
||||
|
@ -130,7 +133,7 @@ def todo(inp, nick='', chan='', db=None, notice=None, bot=None):
|
|||
if not row:
|
||||
notice("No such entry.")
|
||||
return
|
||||
notice("[%d]: %s: %s" % (index, row[0], row[1]))
|
||||
notice("[{}]: {}: {}".format(index, row[0], row[1]))
|
||||
elif cmd == 'del' or cmd == 'delete' or cmd == 'remove':
|
||||
if not len(args):
|
||||
return "error"
|
||||
|
@ -146,7 +149,7 @@ def todo(inp, nick='', chan='', db=None, notice=None, bot=None):
|
|||
|
||||
rows = db_del(db, nick, index)
|
||||
|
||||
notice("Deleted %d entries" % rows.rowcount)
|
||||
notice("Deleted {} entries".format(rows.rowcount))
|
||||
elif cmd == 'list':
|
||||
limit = -1
|
||||
|
||||
|
@ -163,11 +166,11 @@ def todo(inp, nick='', chan='', db=None, notice=None, bot=None):
|
|||
found = False
|
||||
|
||||
for (index, row) in enumerate(rows):
|
||||
notice("[%d]: %s: %s" % (index, row[0], row[1]))
|
||||
notice("[{}]: {}: {}".format(index, row[0], row[1]))
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
notice("%s has no entries." % nick)
|
||||
notice("{} has no entries.".format(nick))
|
||||
elif cmd == 'search':
|
||||
if not len(args):
|
||||
notice("No search query given!")
|
||||
|
@ -178,11 +181,11 @@ def todo(inp, nick='', chan='', db=None, notice=None, bot=None):
|
|||
found = False
|
||||
|
||||
for (index, row) in enumerate(rows):
|
||||
notice("[%d]: %s: %s" % (index, row[0], row[1]))
|
||||
notice("[{}]: {}: {}".format(index, row[0], row[1]))
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
notice("%s has no matching entries for: %s" % (nick, query))
|
||||
notice("{} has no matching entries for: {}".format(nick, query))
|
||||
|
||||
else:
|
||||
notice("Unknown command: %s" % cmd)
|
||||
notice("Unknown command: {}".format(cmd))
|
|
@ -0,0 +1,29 @@
|
|||
from bs4 import BeautifulSoup
|
||||
|
||||
from util import hook, http, web
|
||||
|
||||
|
||||
user_url = "http://osrc.dfm.io/{}"
|
||||
|
||||
|
||||
@hook.command
|
||||
def osrc(inp):
|
||||
"""osrc <github user> -- Gets an Open Source Report Card for <github user>"""
|
||||
|
||||
user_nick = inp.strip()
|
||||
url = user_url.format(user_nick)
|
||||
|
||||
try:
|
||||
soup = http.get_soup(url)
|
||||
except (http.HTTPError, http.URLError):
|
||||
return "Couldn't find any stats for this user."
|
||||
|
||||
report = soup.find("div", {"id": "description"}).find("p").get_text()
|
||||
|
||||
# Split and join to remove all the excess whitespace, slice the
|
||||
# string to remove the trailing full stop.
|
||||
report = " ".join(report.split())[:-1]
|
||||
|
||||
short_url = web.try_isgd(url)
|
||||
|
||||
return "{} - {}".format(report, short_url)
|
|
@ -1,12 +1,15 @@
|
|||
# based on password generation code by TheNoodle
|
||||
from util import hook
|
||||
# TODO: Add some kind of pronounceable password generation
|
||||
# TODO: Improve randomness
|
||||
import string
|
||||
import random
|
||||
|
||||
from util import hook
|
||||
|
||||
|
||||
@hook.command
|
||||
def password(inp, notice=None):
|
||||
"password <length> [types] -- Generates a password of <length> (default 10). [types] can include 'alpha', 'no caps', 'numeric', 'symbols' or any combination of the inp, eg. 'numbers symbols'"
|
||||
"""password <length> [types] -- Generates a password of <length> (default 10).
|
||||
[types] can include 'alpha', 'no caps', 'numeric', 'symbols' or any combination of the inp, eg. 'numbers symbols'"""
|
||||
okay = []
|
||||
|
||||
# find the length needed for the password
|
||||
|
@ -30,17 +33,18 @@ def password(inp, notice=None):
|
|||
|
||||
# add symbols
|
||||
if "symbol" in inp:
|
||||
sym = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '=', '_', '+', '[', ']', '{', '}', '\\', '|', ';', ':', "'", '.', '>', ',', '<', '/', '?', '`', '~', '"']
|
||||
sym = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '=', '_', '+', '[', ']', '{', '}', '\\', '|', ';',
|
||||
':', "'", '.', '>', ',', '<', '/', '?', '`', '~', '"']
|
||||
okay += okay + sym
|
||||
|
||||
# defaults to lowercase alpha password if the okay list is empty
|
||||
if not okay:
|
||||
okay = okay + list(string.ascii_lowercase)
|
||||
|
||||
password = ""
|
||||
pw = ""
|
||||
|
||||
# generates password
|
||||
for x in range(length):
|
||||
password = password + random.choice(okay)
|
||||
pw = pw + random.choice(okay)
|
||||
|
||||
notice(password)
|
||||
notice(pw)
|
|
@ -0,0 +1,12 @@
|
|||
from util import hook, web
|
||||
|
||||
|
||||
@hook.command(adminonly=True)
|
||||
def plpaste(inp):
|
||||
if "/" in inp and inp.split("/")[0] != "util":
|
||||
return "Invalid input"
|
||||
try:
|
||||
with open("plugins/%s.py" % inp) as f:
|
||||
return web.haste(f.read(), ext='py')
|
||||
except IOError:
|
||||
return "Plugin not found (must be in plugins folder)"
|
|
@ -0,0 +1,56 @@
|
|||
# coding=utf-8
|
||||
import re
|
||||
import random
|
||||
|
||||
from util import hook
|
||||
|
||||
|
||||
potatoes = ['AC Belmont', 'AC Blue Pride', 'AC Brador', 'AC Chaleur', 'AC Domino', 'AC Dubuc', 'AC Glacier Chip',
|
||||
'AC Maple Gold', 'AC Novachip', 'AC Peregrine Red', 'AC Ptarmigan', 'AC Red Island', 'AC Saguenor',
|
||||
'AC Stampede Russet', 'AC Sunbury', 'Abeille', 'Abnaki', 'Acadia', 'Acadia Russet', 'Accent',
|
||||
'Adirondack Blue', 'Adirondack Red', 'Adora', 'Agria', 'All Blue', 'All Red', 'Alpha', 'Alta Russet',
|
||||
'Alturas Russet', 'Amandine', 'Amisk', 'Andover', 'Anoka', 'Anson', 'Aquilon', 'Arran Consul', 'Asterix',
|
||||
'Atlantic', 'Austrian Crescent', 'Avalanche', 'Banana', 'Bannock Russet', 'Batoche', 'BeRus',
|
||||
'Belle De Fonteney', 'Belleisle', 'Bintje', 'Blossom', 'Blue Christie', 'Blue Mac', 'Brigus',
|
||||
'Brise du Nord', 'Butte', 'Butterfinger', 'Caesar', 'CalWhite', 'CalRed', 'Caribe', 'Carlingford',
|
||||
'Carlton', 'Carola', 'Cascade', 'Castile', 'Centennial Russet', 'Century Russet', 'Charlotte', 'Cherie',
|
||||
'Cherokee', 'Cherry Red', 'Chieftain', 'Chipeta', 'Coastal Russet', 'Colorado Rose', 'Concurrent',
|
||||
'Conestoga', 'Cowhorn', 'Crestone Russet', 'Crispin', 'Cupids', 'Daisy Gold', 'Dakota Pearl', 'Defender',
|
||||
'Delikat', 'Denali', 'Desiree', 'Divina', 'Dundrod', 'Durango Red', 'Early Rose', 'Elba', 'Envol',
|
||||
'Epicure', 'Eramosa', 'Estima', 'Eva', 'Fabula', 'Fambo', 'Fremont Russet', 'French Fingerling',
|
||||
'Frontier Russet', 'Fundy', 'Garnet Chile', 'Gem Russet', 'GemStar Russet', 'Gemchip', 'German Butterball',
|
||||
'Gigant', 'Goldrush', 'Granola', 'Green Mountain', 'Haida', 'Hertha', 'Hilite Russet', 'Huckleberry',
|
||||
'Hunter', 'Huron', 'IdaRose', 'Innovator', 'Irish Cobbler', 'Island Sunshine', 'Ivory Crisp',
|
||||
'Jacqueline Lee', 'Jemseg', 'Kanona', 'Katahdin', 'Kennebec', "Kerr's Pink", 'Keswick', 'Keuka Gold',
|
||||
'Keystone Russet', 'King Edward VII', 'Kipfel', 'Klamath Russet', 'Krantz', 'LaRatte', 'Lady Rosetta',
|
||||
'Latona', 'Lemhi Russet', 'Liberator', 'Lili', 'MaineChip', 'Marfona', 'Maris Bard', 'Maris Piper',
|
||||
'Matilda', 'Mazama', 'McIntyre', 'Michigan Purple', 'Millenium Russet', 'Mirton Pearl', 'Modoc', 'Mondial',
|
||||
'Monona', 'Morene', 'Morning Gold', 'Mouraska', 'Navan', 'Nicola', 'Nipigon', 'Niska', 'Nooksack',
|
||||
'NorValley', 'Norchip', 'Nordonna', 'Norgold Russet', 'Norking Russet', 'Norland', 'Norwis', 'Obelix',
|
||||
'Ozette', 'Peanut', 'Penta', 'Peribonka', 'Peruvian Purple', 'Pike', 'Pink Pearl', 'Prospect', 'Pungo',
|
||||
'Purple Majesty', 'Purple Viking', 'Ranger Russet', 'Reba', 'Red Cloud', 'Red Gold', 'Red La Soda',
|
||||
'Red Pontiac', 'Red Ruby', 'Red Thumb', 'Redsen', 'Rocket', 'Rose Finn Apple', 'Rose Gold', 'Roselys',
|
||||
'Rote Erstling', 'Ruby Crescent', 'Russet Burbank', 'Russet Legend', 'Russet Norkotah', 'Russet Nugget',
|
||||
'Russian Banana', 'Saginaw Gold', 'Sangre', 'Sant<EFBFBD>', 'Satina', 'Saxon', 'Sebago', 'Shepody', 'Sierra',
|
||||
'Silverton Russet', 'Simcoe', 'Snowden', 'Spunta', "St. John's", 'Summit Russet', 'Sunrise', 'Superior',
|
||||
'Symfonia', 'Tolaas', 'Trent', 'True Blue', 'Ulla', 'Umatilla Russet', 'Valisa', 'Van Gogh', 'Viking',
|
||||
'Wallowa Russet', 'Warba', 'Western Russet', 'White Rose', 'Willamette', 'Winema', 'Yellow Finn',
|
||||
'Yukon Gold']
|
||||
|
||||
|
||||
@hook.command
|
||||
def potato(inp, action=None):
|
||||
"""potato <user> - Makes <user> a tasty little potato."""
|
||||
inp = inp.strip()
|
||||
|
||||
if not re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()):
|
||||
return "I cant make a tasty potato for that user!"
|
||||
|
||||
potato_type = random.choice(potatoes)
|
||||
size = random.choice(['small', 'little', 'mid-sized', 'medium-sized', 'large', 'gigantic'])
|
||||
flavor = random.choice(['tasty', 'delectable', 'delicious', 'yummy', 'toothsome', 'scrumptious', 'luscious'])
|
||||
method = random.choice(['bakes', 'fries', 'boils', 'roasts'])
|
||||
side_dish = random.choice(['side salad', 'dollop of sour cream', 'piece of chicken', 'bowl of shredded bacon'])
|
||||
|
||||
action("{} a {} {} {} potato for {} and serves it with a small {}!".format(method, flavor, size, potato_type, inp,
|
||||
side_dish))
|
|
@ -0,0 +1,38 @@
|
|||
import datetime
|
||||
|
||||
from util import hook, http, timesince
|
||||
|
||||
|
||||
@hook.command("scene")
|
||||
@hook.command
|
||||
def pre(inp):
|
||||
"""pre <query> -- searches scene releases using orlydb.com"""
|
||||
|
||||
try:
|
||||
h = http.get_html("http://orlydb.com/", q=inp)
|
||||
except http.HTTPError as e:
|
||||
return 'Unable to fetch results: {}'.format(e)
|
||||
|
||||
results = h.xpath("//div[@id='releases']/div/span[@class='release']/..")
|
||||
|
||||
if not results:
|
||||
return "No results found."
|
||||
|
||||
result = results[0]
|
||||
|
||||
date = result.xpath("span[@class='timestamp']/text()")[0]
|
||||
section = result.xpath("span[@class='section']//text()")[0]
|
||||
name = result.xpath("span[@class='release']/text()")[0]
|
||||
|
||||
# parse date/time
|
||||
date = datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
|
||||
date_string = date.strftime("%d %b %Y")
|
||||
since = timesince.timesince(date)
|
||||
|
||||
size = result.xpath("span[@class='inforight']//text()")
|
||||
if size:
|
||||
size = ' - ' + size[0].split()[0]
|
||||
else:
|
||||
size = ''
|
||||
|
||||
return '{} - {}{} - {} ({} ago)'.format(section, name, size, date_string, since)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue