├── .editorconfig
├── .gitattributes
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── Vagrantfile
├── cloudbot
├── __init__.py
├── __main__.py
├── bot.py
├── client.py
├── clients
│ ├── __init__.py
│ └── irc.py
├── config.py
├── event.py
├── hook.py
├── permissions.py
├── plugin.py
├── reloader.py
└── util
│ ├── __init__.py
│ ├── async_util.py
│ ├── colors.py
│ ├── database.py
│ ├── filesize.py
│ ├── formatting.py
│ ├── func_utils.py
│ ├── http.py
│ ├── pager.py
│ ├── parsers
│ ├── __init__.py
│ └── irc.py
│ ├── sequence.py
│ ├── test
│ ├── test_colors.py
│ ├── test_database.py
│ ├── test_filesize.py
│ ├── test_formatting.py
│ ├── test_timeformat.py
│ └── test_tokenbucket.py
│ ├── textgen.py
│ ├── timeformat.py
│ ├── timeparse.py
│ ├── tokenbucket.py
│ └── web.py
├── config.default.json
├── data
├── 8ball_responses.txt
├── attacks
│ ├── bdsm.json
│ ├── bite.json
│ ├── clinton.json
│ ├── compliment.json
│ ├── fight.json
│ ├── flirt.json
│ ├── glomp.json
│ ├── highfive.json
│ ├── hug.json
│ ├── insult.json
│ ├── kill.json
│ ├── lart.json
│ ├── lurve.json
│ ├── nk.json
│ ├── present.json
│ ├── slap.json
│ ├── spank.json
│ ├── strax.json
│ └── trump.json
├── book_puns.txt
├── cheers.txt
├── confucious.txt
├── do_it.txt
├── drinks.json
├── fmk.txt
├── foaas.json
├── food
│ ├── beer.json
│ ├── brekkie.json
│ ├── burger.json
│ ├── cake.json
│ ├── cereal.json
│ ├── cheese.json
│ ├── chicken.json
│ ├── chocolate.json
│ ├── coffee.json
│ ├── cookies.json
│ ├── donut.json
│ ├── doobie.json
│ ├── halal.json
│ ├── icecream.json
│ ├── kebab.json
│ ├── keto.json
│ ├── kosher.json
│ ├── milkshake.json
│ ├── muffin.json
│ ├── noodles.json
│ ├── nugget.json
│ ├── pancake.json
│ ├── pasta.json
│ ├── pie.json
│ ├── pizza.json
│ ├── potato.json
│ ├── rice.json
│ ├── sandwich.json
│ ├── scone.json
│ ├── soup.json
│ ├── steak.json
│ ├── sushi.json
│ ├── taco.json
│ ├── tea.json
│ └── wine.json
├── fortunes.txt
├── gnomecards.json
├── hookup.json
├── kenm.txt
├── kero.txt
├── lawyerjoke.txt
├── leet.json
├── lenny.json
├── name_files
│ ├── dragons.json
│ ├── dwarves.json
│ ├── elves_female.json
│ ├── elves_male.json
│ ├── fantasy.json
│ ├── female.json
│ ├── general.json
│ ├── hobbits.json
│ ├── inns.json
│ ├── items.json
│ ├── male.json
│ ├── narn.json
│ └── warrior_cats.json
├── one_liners.txt
├── password_words.txt
├── puns.txt
├── reaction_macros.json
├── topicchange.txt
├── wisdom.txt
└── yo_momma.txt
├── docs
├── README.md
├── dev
│ └── main.md
└── user
│ ├── commands.md
│ ├── configuration.md
│ ├── googlecustomsearch_id.md
│ ├── googledevconsole_api.md
│ ├── img
│ ├── cse_1.png
│ ├── cse_2.png
│ ├── cse_3.png
│ ├── gdev_1.png
│ ├── gdev_2.png
│ ├── gdev_3.png
│ ├── gdev_4.png
│ ├── gdev_5.png
│ ├── gdev_6.png
│ ├── gdev_7.png
│ ├── gdev_8.png
│ ├── oc_1.png
│ ├── oc_2.png
│ ├── oc_3.png
│ └── wn_1.png
│ ├── main_user.md
│ ├── octopart_api.md
│ ├── optout.md
│ └── wordnik_api.md
├── format_json.py
├── plugins
├── __init__.py
├── admin_bot.py
├── admin_channel.py
├── amazon.py
├── animal_gifs.py
├── attacks.py
├── autojoin.py
├── badwords.py
├── bible.py
├── bing.py
├── books.py
├── brainfuck.py
├── brew.py
├── cats.py
├── chain.py
├── chan_track.py
├── chatbot.py
├── cheer.py
├── core
│ ├── __init__.py
│ ├── cap.py
│ ├── chan_log.py
│ ├── check_conn.py
│ ├── core_connect.py
│ ├── core_ctcp.py
│ ├── core_hooks.py
│ ├── core_misc.py
│ ├── core_out.py
│ ├── core_sieve.py
│ ├── core_tracker.py
│ ├── help.py
│ ├── optout.py
│ ├── plugin_control.py
│ ├── regex_chans.py
│ └── server_info.py
├── correction.py
├── cryptocurrency.py
├── cypher.py
├── deals.py
├── dig.py
├── dogpile.py
├── domainr.py
├── dragonvale.py
├── dramatica.py
├── drinks.py
├── duckhunt.py
├── eightball.py
├── etymology.py
├── fact.py
├── factoids.py
├── feeds.py
├── fishbans.py
├── flip.py
├── fmk.py
├── foaas.py
├── foods.py
├── fortune.py
├── gaming.py
├── geoip.py
├── giphy.py
├── github.py
├── gnomeagainsthumanity.py
├── google.py
├── google_cse.py
├── google_translate.py
├── googleurlparse.py
├── grab.py
├── herald.py
├── history.py
├── hook_stats.py
├── hookup.py
├── horoscope.py
├── ignore.py
├── imdb.py
├── imgur.py
├── issafe.py
├── jokes.py
├── karma.py
├── kenm.py
├── lastfm.py
├── lenny.py
├── librefm.py
├── link_announcer.py
├── linux.py
├── lmgtfy.py
├── locate.py
├── log.py
├── lyricsnmusic.py
├── metacritic.py
├── metars.py
├── minecraft_ping.py
├── minecraft_status.py
├── minecraft_user.py
├── minecraft_wiki.py
├── mock.py
├── monsterhunt.py
├── myfitnesspal.py
├── mylife.py
├── name_generator.py
├── newegg.py
├── notes.py
├── octopart.py
├── pagecheck.py
├── password.py
├── piglatin.py
├── ping.py
├── plpaste.py
├── poll.py
├── profile.py
├── profiling.py
├── quote.py
├── quran.py
├── randomusefulwebsites.py
├── reactions.py
├── recipe.py
├── reddit.py
├── reddit_info.py
├── remind.py
├── rottentomatoes.py
├── rua.py
├── sasl.py
├── scene.py
├── shorten.py
├── shrug.py
├── snopes.py
├── soundcloud.py
├── speedtest.py
├── spellcheck.py
├── sportscores.py
├── spotify.py
├── steam_store.py
├── steam_user.py
├── steamdb.py
├── stock.py
├── suggest.py
├── system.py
├── tell.py
├── thefuckingweather.py
├── time_plugin.py
├── topicchange.py
├── tvdb.py
├── twitch.py
├── twitter.py
├── urban.py
├── utility.py
├── validate.py
├── vimeo.py
├── voat.py
├── weather.py
├── whois.py
├── wikipedia.py
├── wolframalpha.py
├── wordnik.py
├── wyr.py
├── xkcd.py
├── yandex_translate.py
├── yelling.py
└── youtube.py
├── requirements.txt
├── tests
├── core_tests
│ ├── __init__.py
│ └── test_plugin_hooks.py
└── plugin_tests
│ ├── __init__.py
│ ├── test_fishbans.py
│ └── test_link_announcer.py
├── travis
├── pylintrc
├── requirements.txt
└── test_json.py
└── vagrant-bootstrap.sh
/.editorconfig:
--------------------------------------------------------------------------------
1 | # CloudBot editor configuration normalization
2 | # Copied from Drupal (GPL)
3 | # @see http://editorconfig.org/
4 |
5 | # This is the top-most .editorconfig file; do not search in parent directories.
6 | root = true
7 |
8 | # All files.
9 | [*]
10 | end_of_line = LF
11 | indent_style = space
12 | indent_size = 4
13 |
14 | # Not in the spec yet:
15 | # @see https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # default all files as text with lf
2 | * text eol=lf
3 |
4 | # mmdb files are binary
5 | *.mmdb binary
6 |
7 | # PNGs are also binary (it broke the docs)
8 | *.png binary
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | .Python
10 | env/
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | lib/
17 | lib64/
18 | parts/
19 | sdist/
20 | var/
21 | *.egg-info/
22 | .installed.cfg
23 | *.egg
24 |
25 | # PyInstaller
26 | # Usually these files are written by a python script from a template
27 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
28 | *.manifest
29 | *.spec
30 |
31 | # Installer logs
32 | pip-log.txt
33 | pip-delete-this-directory.txt
34 |
35 | # Unit test / coverage reports
36 | htmlcov/
37 | .tox/
38 | .coverage
39 | .cache
40 | nosetests.xml
41 | coverage.xml
42 |
43 | # Translations
44 | *.mo
45 | *.pot
46 |
47 | # Sphinx documentation
48 | docs/_build/
49 |
50 | # PyBuilder
51 | target/
52 |
53 | # Cloudbot
54 | persist/
55 | logs/
56 | config.json
57 | *.db
58 | *.mmdb
59 | *.log
60 | .idea/
61 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "3.4"
4 | - "3.5"
5 | - "3.6"
6 | - "3.7-dev"
7 | - "nightly"
8 | - "pypy3"
9 |
10 | cache: pip
11 |
12 | install:
13 | - "sudo apt-get update -q"
14 | - "sudo apt-get install -y python3-lxml libenchant-dev"
15 | - "pip install -r ./travis/requirements.txt"
16 |
17 | script:
18 | - "py.test . -R : -v --cov . --cov-report term-missing --pylint --pylint-rcfile=travis/pylintrc"
19 |
20 | after_success:
21 | - "coveralls"
22 |
23 | env:
24 | - PYTHONPATH=.
25 | - PYTHONPATH=. PYTHONASYNCIODEBUG=1
26 |
27 | matrix:
28 | allow_failures:
29 | - python: "3.7-dev"
30 | - python: "nightly"
31 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## Changelog
2 |
3 | ## 1.0.9
4 | TBA
5 |
6 | ### 1.0.8
7 | This update is pretty big. Be warned.
8 | * Improved flip command.
9 | * Added new time command that gets time for location.
10 | * Added locate command that locates a place on Google Maps.
11 | * Change weather command to use new location API from the two above.
12 | * Added more kill messages.
13 | * Revamp lastfm with more commands and better memory.
14 | * Add new poll command. Still not perfect.
15 | * Replaced old dictionary plugin with new Wordnik plugin.
16 | * Revamped Soundcloud command.
17 | * Revamped chatbot command.
18 | * Switched back to google search.
19 | * Added new issafe plugin.
20 | * And a whole lot of minor tweaks and fixes.
21 |
22 | ### 1.0.7.1
23 | * Security fixes.
24 |
25 | ### 1.0.7
26 | * Added new "Would you rather" plugin.
27 |
28 | ### 1.0.6
29 | * Added pig latin translator, requires new *nltk* module
30 | * Added reminder command
31 | * Added new periodic hook (does not support reloading properly yet, so use with caution)
32 | * Added priority sorting to sieve hooks
33 | * Started work on new documentation for 1.1
34 | * Did some minor internal refactoring
35 |
36 | **1.0.5** - Fix geoip for queries with no region, fix youtube bug, add flip command
37 |
38 | **1.0.4** - Adjust ratelimiter cleanup task, add octopart API key, fix brainfuck, sort mcstatus output.
39 |
40 | **1.0.3** - More minor changes to plugins, fixed rate-limiting properly, banished SCP to CloudBotIRC/Plugins, added wildcard support to permissions (note: don't use this yet, it's still not entirely finalized!)
41 |
42 | **1.0.2** - Minor internal changes and fixes, banished minecraft_bukget and worldofwarcraft to CloudBotIRC/Plugins
43 |
44 | **1.0.1** - Fix history.py tracking
45 |
46 | **1.0.0** - Initial stable release
47 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | VAGRANTFILE_API_VERSION = "2"
2 |
3 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
4 | config.vm.box = "ubuntu/trusty32"
5 | config.vm.provision :shell, path: "vagrant-bootstrap.sh"
6 | end
7 |
--------------------------------------------------------------------------------
/cloudbot/clients/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/cloudbot/clients/__init__.py
--------------------------------------------------------------------------------
/cloudbot/config.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 | import os
4 | import sys
5 | import time
6 | from collections import OrderedDict
7 |
8 | logger = logging.getLogger("cloudbot")
9 |
10 |
11 | class Config(OrderedDict):
12 | """
13 | :type filename: str
14 | :type path: str
15 | :type bot: cloudbot.bot.CloudBot
16 | """
17 |
18 | def __init__(self, bot, *args, **kwargs):
19 | """
20 | :type bot: cloudbot.bot.CloudBot
21 | :type args: list
22 | :type kwargs: dict
23 | """
24 | super().__init__(*args, **kwargs)
25 | self.filename = "config.json"
26 | self.path = os.path.abspath(self.filename)
27 | self.bot = bot
28 | self.update(*args, **kwargs)
29 |
30 | # populate self with config data
31 | self.load_config()
32 |
33 | def load_config(self):
34 | """(re)loads the bot config from the config file"""
35 | if not os.path.exists(self.path):
36 | # if there is no config, show an error and die
37 | logger.critical("No config file found, bot shutting down!")
38 | print("No config file found! Bot shutting down in five seconds.")
39 | print("Copy 'config.default.json' to 'config.json' for defaults.")
40 | print("For help, see http://git.io/cloudbotirc. Thank you for using CloudBot!")
41 | time.sleep(5)
42 | sys.exit()
43 |
44 | with open(self.path) as f:
45 | data = json.load(f, object_pairs_hook=OrderedDict)
46 |
47 | self.update(data)
48 | logger.debug("Config loaded from file.")
49 |
50 | # reload permissions
51 | if self.bot.connections:
52 | for connection in self.bot.connections.values():
53 | connection.permissions.reload()
54 |
55 | def save_config(self):
56 | """saves the contents of the config dict to the config file"""
57 | with open(self.path, 'w') as f:
58 | json.dump(self, f, indent=4)
59 |
60 | logger.info("Config saved to file.")
61 |
--------------------------------------------------------------------------------
/cloudbot/util/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/cloudbot/util/__init__.py
--------------------------------------------------------------------------------
/cloudbot/util/async_util.py:
--------------------------------------------------------------------------------
1 | """
2 | Wraps various asyncio functions
3 | """
4 |
5 | import asyncio
6 | import sys
7 | from functools import partial
8 |
9 | from cloudbot.util.func_utils import call_with_args
10 |
11 |
12 | def wrap_future(fut, *, loop=None):
13 | """
14 | Wraps asyncio.async()/asyncio.ensure_future() depending on the python version
15 | :param fut: The awaitable, future, or coroutine to wrap
16 | :param loop: The loop to run in
17 | :return: The wrapped future
18 | """
19 | if sys.version_info < (3, 4, 4):
20 | # This is to avoid a SyntaxError on 3.7.0a2+
21 | func = getattr(asyncio, "async")
22 | else:
23 | func = asyncio.ensure_future
24 |
25 | return func(fut, loop=loop) # pylint: disable=locally-disabled, deprecated-method
26 |
27 |
28 | @asyncio.coroutine
29 | def run_func(loop, func, *args, **kwargs):
30 | part = partial(func, *args, **kwargs)
31 | if asyncio.iscoroutine(func) or asyncio.iscoroutinefunction(func):
32 | return (yield from part())
33 | else:
34 | return (yield from loop.run_in_executor(None, part))
35 |
36 |
37 | @asyncio.coroutine
38 | def run_func_with_args(loop, func, arg_data, executor=None):
39 | if asyncio.iscoroutine(func):
40 | raise TypeError('A coroutine function or a normal, non-async callable are required')
41 |
42 | if asyncio.iscoroutinefunction(func):
43 | coro = call_with_args(func, arg_data)
44 | else:
45 | coro = loop.run_in_executor(executor, call_with_args, func, arg_data)
46 |
47 | return (yield from coro)
48 |
49 |
50 | def run_coroutine_threadsafe(coro, loop):
51 | """
52 | Runs a coroutine in a threadsafe manner
53 | :type coro: coroutine
54 | :type loop: asyncio.AbstractEventLoop
55 | """
56 | if not asyncio.iscoroutine(coro):
57 | raise TypeError('A coroutine object is required')
58 |
59 | if sys.version_info < (3, 5, 1):
60 | loop.call_soon_threadsafe(partial(wrap_future, coro, loop=loop))
61 | else:
62 | asyncio.run_coroutine_threadsafe(coro, loop)
63 |
64 |
65 | def create_future(loop=None):
66 | if loop is None:
67 | loop = asyncio.get_event_loop()
68 |
69 | if sys.version_info < (3, 5, 2):
70 | return asyncio.Future(loop=loop)
71 |
72 | return loop.create_future()
73 |
--------------------------------------------------------------------------------
/cloudbot/util/database.py:
--------------------------------------------------------------------------------
1 | """
2 | database - contains variables set by cloudbot to be easily access
3 | """
4 |
5 | # this is assigned in the CloudBot so that its recreated when the bot restarts
6 | metadata = None
7 | base = None
8 |
--------------------------------------------------------------------------------
/cloudbot/util/func_utils.py:
--------------------------------------------------------------------------------
1 | import inspect
2 |
3 |
4 | class ParameterError(Exception):
5 | def __init__(self, name, valid_args):
6 | self.__init__(name, list(valid_args))
7 |
8 | def __str__(self):
9 | return "'{}' is not a valid parameter, valid parameters are: {}".format(self.args[0], self.args[1])
10 |
11 |
12 | def call_with_args(func, arg_data):
13 | sig = inspect.signature(func)
14 | try:
15 | args = [arg_data[key] for key in sig.parameters.keys() if not key.startswith('_')]
16 | except KeyError as e:
17 | raise ParameterError(e.args[0], arg_data.keys()) from e
18 |
19 | return func(*args)
20 |
--------------------------------------------------------------------------------
/cloudbot/util/pager.py:
--------------------------------------------------------------------------------
1 | from threading import RLock
2 |
3 | from cloudbot.util.sequence import chunk_iter
4 |
5 |
6 | class Pager:
7 | """Multiline pager
8 |
9 | Takes a string with newlines and paginates it to certain size chunks
10 | """
11 |
12 | @classmethod
13 | def from_multiline_string(cls, s):
14 | return cls(s.splitlines())
15 |
16 | def __init__(self, lines, chunk_size=2):
17 | # This lock should always be acquired when accessing data from this object
18 | # Added here due to extensive use of threads throughout plugins
19 | self.lock = RLock()
20 | self.chunk_size = chunk_size
21 | self.chunks = tuple(chunk_iter(lines, self.chunk_size))
22 | self.current_pos = 0
23 |
24 | def format_chunk(self, chunk, pagenum):
25 | chunk = list(chunk)
26 | if len(self.chunks) > 1:
27 | chunk[-1] += " (page {}/{})".format(pagenum + 1, len(self.chunks))
28 |
29 | return chunk
30 |
31 | def next(self):
32 | with self.lock:
33 | if self.current_pos >= len(self.chunks):
34 | return None
35 |
36 | chunk = self[self.current_pos]
37 | self.current_pos += 1
38 |
39 | return chunk
40 |
41 | def get(self, index):
42 | """Get a specific page"""
43 | return self[index]
44 |
45 | def __getitem__(self, item):
46 | """Get a specific page"""
47 | with self.lock:
48 | chunk = self.chunks[item]
49 | return self.format_chunk(chunk, item)
50 |
51 | def __len__(self):
52 | with self.lock:
53 | return len(self.chunks)
54 |
55 |
56 | def paginated_list(data, delim=" \u2022 ", suffix='...', max_len=256, page_size=2):
57 | lines = [""]
58 | for item in data:
59 | if len(item) > max_len:
60 | # The length of a single item is longer then our max line length, split it
61 | lines.append(item[:max_len])
62 | lines.append(item[max_len:])
63 | elif len(lines[-1]) + len(item) > max_len:
64 | lines.append(item)
65 | else:
66 | if lines[-1]:
67 | lines[-1] += delim
68 |
69 | lines[-1] += item
70 |
71 | formatted_lines = []
72 | while lines:
73 | line = lines.pop(0)
74 | formatted_lines.append("{}{}".format(line, suffix if lines else ""))
75 |
76 | return Pager(formatted_lines, chunk_size=page_size)
77 |
--------------------------------------------------------------------------------
/cloudbot/util/parsers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/cloudbot/util/parsers/__init__.py
--------------------------------------------------------------------------------
/cloudbot/util/sequence.py:
--------------------------------------------------------------------------------
1 | """
2 | Sequence utilities - Various util functions for working with lists, sets, tuples, etc
3 | """
4 |
5 |
6 | def chunk_iter(data, chunk_size):
7 | """
8 | Splits a sequence in to chunks
9 | :param data: The sequence to split
10 | :param chunk_size: The maximum size of each chunk
11 | :return: An iterable of all the chunks of the sequence
12 | """
13 | for i in range(0, len(data), chunk_size):
14 | yield data[i:i + chunk_size]
15 |
--------------------------------------------------------------------------------
/cloudbot/util/test/test_colors.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from cloudbot.util.colors import parse, strip, get_available_colours, get_available_formats, get_color, get_format, \
4 | _convert, strip_irc, strip_all, IRC_COLOUR_DICT
5 |
6 | test_input = "The quick $(brown, red)brown$(clear) fox$(fake) jumps over the $(bold)lazy dog$(clear)."
7 |
8 | test_parse_output = "The quick \x0305,04brown\x0f fox jumps over the \x02lazy dog\x0f."
9 | test_strip_output = "The quick brown fox jumps over the lazy dog."
10 |
11 | test_strip_irc_input = "\x02I am $(bold)bold\x02"
12 | test_strip_irc_result = "I am $(bold)bold"
13 | test_strip_all_result = "I am bold"
14 |
15 |
16 | def test_parse():
17 | assert parse(test_input) == test_parse_output
18 |
19 |
20 | def test_strip():
21 | assert strip(test_input) == test_strip_output
22 | assert strip_irc(test_strip_irc_input) == test_strip_irc_result
23 | assert strip_all(test_strip_irc_input) == test_strip_all_result
24 |
25 |
26 | def test_available_colors():
27 | assert "dark_grey" in get_available_colours()
28 |
29 |
30 | def test_available_formats():
31 | assert "bold" in get_available_formats()
32 |
33 |
34 | def test_invalid_color():
35 | with pytest.raises(KeyError) as excinfo:
36 | get_color("cake")
37 | assert 'not in the list of available colours' in str(excinfo.value)
38 |
39 |
40 | def test_invalid_format():
41 | with pytest.raises(KeyError) as excinfo:
42 | get_format("cake")
43 | assert 'not found in the list of available formats' in str(excinfo.value)
44 |
45 |
46 | def test_get_color():
47 | assert get_color("red") == "\x0304"
48 | assert get_color("red", return_formatted=False) == "04"
49 |
50 |
51 | def test_get_random_color():
52 | assert get_color("random") in ["\x03" + i for i in IRC_COLOUR_DICT.values()]
53 | assert get_color("random", return_formatted=False) in list(IRC_COLOUR_DICT.values())
54 |
55 |
56 | def test_get_format():
57 | assert get_format("bold") == "\x02"
58 |
59 |
60 | def test_convert():
61 | assert _convert("$(red, green)") == "\x0304,09"
62 | assert _convert("$(red, bold)") == "\x0304\x02"
63 | assert _convert("$(red)") == "\x0304"
64 | assert _convert("$(bold)") == "\x02"
65 | assert _convert("cats") == "cats"
66 |
--------------------------------------------------------------------------------
/cloudbot/util/test/test_database.py:
--------------------------------------------------------------------------------
1 | from cloudbot.util.database import metadata, base
2 |
3 |
4 | def test_database():
5 | assert metadata is None
6 | assert base is None
7 |
--------------------------------------------------------------------------------
/cloudbot/util/test/test_filesize.py:
--------------------------------------------------------------------------------
1 | import cloudbot.util.filesize as fs
2 | from cloudbot.util.filesize import size, si, verbose
3 |
4 |
5 | def test_size():
6 | # Using the traditional system, where a factor of 1024 is used
7 | assert size(10) == "10B"
8 | assert size(100) == "100B"
9 | assert size(1000) == "1000B"
10 | assert size(2000) == "1K"
11 | assert size(10000) == "9K"
12 | assert size(20000) == "19K"
13 | assert size(100000) == "97K"
14 | assert size(200000) == "195K"
15 | assert size(1000000) == "976K"
16 | assert size(2000000) == "1M"
17 |
18 |
19 | def test_size_verbose():
20 | # Using the verbose system, where a factor of 1024 is used
21 | assert size(1, system=verbose) == "1 byte"
22 | assert size(1000, system=verbose) == "1000 bytes"
23 | assert size(2000, system=verbose) == "1 kilobyte"
24 | assert size(10000, system=verbose) == "9 kilobytes"
25 | assert size(2000000, system=verbose) == "1 megabyte"
26 | assert size(30000000, system=verbose) == "28 megabytes"
27 |
28 |
29 | def test_size_si():
30 | # Using the SI system, with a factor of 1000
31 | assert size(10, system=si) == "10B"
32 | assert size(100, system=si) == "100B"
33 | assert size(1000, system=si) == "1K"
34 | assert size(2000, system=si) == "2K"
35 | assert size(10000, system=si) == "10K"
36 | assert size(20000, system=si) == "20K"
37 | assert size(100000, system=si) == "100K"
38 | assert size(200000, system=si) == "200K"
39 | assert size(1000000, system=si) == "1M"
40 | assert size(2000000, system=si) == "2M"
41 |
42 |
43 | def test_size_alias():
44 | assert size(1, system=fs.V) == "1 byte"
45 |
--------------------------------------------------------------------------------
/cloudbot/util/test/test_timeformat.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from cloudbot.util.timeformat import format_time, time_since, time_until
4 |
5 |
6 | def test_format_time():
7 | # basic
8 | assert format_time(120000) == "1 day, 9 hours and 20 minutes"
9 | assert format_time(120000, simple=True) == "1d 9h 20m"
10 | # count
11 | assert format_time(1200003, count=4) == "13 days, 21 hours, 20 minutes and 3 seconds"
12 | assert format_time(1200000, count=4) == "13 days, 21 hours and 20 minutes"
13 | assert format_time(1200000, count=2) == "13 days and 21 hours"
14 |
15 |
16 | def test_timesince():
17 | then = datetime(2010, 4, 12, 12, 30, 0)
18 | then_timestamp = 1271075400.0
19 | then_future = datetime(2012, 4, 12, 12, 30, 0)
20 | now = datetime(2010, 5, 15, 1, 50, 0)
21 | now_timestamp = 1273888200.0
22 | # timestamp
23 | assert time_since(then_timestamp, now_timestamp) == "1 month and 2 days"
24 | # basic
25 | assert time_since(then, now) == "1 month and 2 days"
26 | # count
27 | assert time_since(then, now, count=3) == "1 month, 2 days and 13 hours"
28 | # future
29 | assert time_since(then_future, now) == "0 minutes"
30 |
31 |
32 | def test_timeuntil():
33 | now = datetime(2010, 4, 12, 12, 30, 0)
34 | future = datetime(2010, 5, 15, 1, 50, 0)
35 | # basic
36 | assert time_until(future, now) == "1 month and 2 days"
37 | # count
38 | assert time_until(future, now, count=3) == "1 month, 2 days and 13 hours"
39 |
--------------------------------------------------------------------------------
/cloudbot/util/test/test_tokenbucket.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | from cloudbot.util.tokenbucket import TokenBucket
4 |
5 |
6 | # noinspection PyProtectedMember
7 | def test_bucket_consume():
8 | bucket = TokenBucket(10, 5)
9 | # larger then capacity
10 | assert bucket.consume(15) is False
11 | # success
12 | assert bucket.consume(10) is True
13 | # check if bucket has no tokens
14 | assert bucket._tokens == 0
15 | # bucket is empty from above, should fail
16 | assert bucket.consume(10) is False
17 |
18 |
19 | # noinspection PyProtectedMember
20 | def test_bucket_advanced():
21 | bucket = TokenBucket(10, 1)
22 | # tokens start at 10
23 | assert bucket._tokens == 10
24 | # empty tokens
25 | assert bucket.empty() is True
26 | # check tokens is 0
27 | assert bucket._tokens == 0
28 | # refill tokens
29 | assert bucket.refill() is True
30 | # check tokens is 10
31 | assert bucket._tokens == 10
32 |
33 |
34 | def test_bucket_regen():
35 | bucket = TokenBucket(10, 10)
36 | # success
37 | assert bucket.consume(10) is True
38 | # sleep
39 | time.sleep(1)
40 | # bucket should be full again and this should succeed
41 | assert bucket.tokens == 10
42 | assert bucket.consume(10) is True
43 |
--------------------------------------------------------------------------------
/cloudbot/util/tokenbucket.py:
--------------------------------------------------------------------------------
1 | """
2 | tokenbucket.py
3 |
4 | A python implementation of the token bucket algorithm.
5 | Adapted from
6 |
7 | Maintainer:
8 | - Luke Rogers
9 |
10 | License:
11 | Python Software Foundation License (PSF)
12 | """
13 |
14 | from time import time
15 |
16 |
17 | class TokenBucket(object):
18 | """An implementation of the token bucket algorithm.
19 | >> bucket = TokenBucket(80, 0.5)
20 | >> bucket.consume(10)
21 | True
22 | >> bucket.consume(90)
23 | False
24 | """
25 |
26 | def __init__(self, _capacity, fill_rate):
27 | """
28 | :param _capacity: The total amount of token the bucket can contain
29 | :param fill_rate: The rate at which tokens regenerate. (fill_rate per second)
30 | """
31 | """tokens is the total tokens in the bucket. fill_rate is the
32 | rate in tokens/second that the bucket will be refilled."""
33 | self.capacity = float(_capacity)
34 | self._tokens = float(_capacity)
35 | self.fill_rate = float(fill_rate)
36 | self.timestamp = time()
37 |
38 | def consume(self, tokens):
39 | """
40 | Consume tokens from the bucket.
41 | :param tokens: The number of tokens to consume
42 | :return true if there were sufficient tokens otherwise false
43 | """
44 | if tokens <= self.tokens:
45 | self._tokens -= tokens
46 | else:
47 | return False
48 | return True
49 |
50 | def refill(self):
51 | """
52 | Sets the current token count to the max capacity
53 | """
54 | self._tokens = self.capacity
55 | return True
56 |
57 | def empty(self):
58 | """
59 | Sets the current token count to zero
60 | """
61 | self._tokens = float(0)
62 | return True
63 |
64 | def get_tokens(self):
65 | """
66 | Calculates and returns the current amount of tokens the bucker contains
67 |
68 | :return Amount of tokens the bucket contains
69 | :rtype Float
70 | """
71 | now = time()
72 | if self._tokens < self.capacity:
73 | delta = self.fill_rate * (now - self.timestamp)
74 | self._tokens = min(self.capacity, self._tokens + delta)
75 | self.timestamp = now
76 | return self._tokens
77 |
78 | tokens = property(get_tokens)
79 |
--------------------------------------------------------------------------------
/data/8ball_responses.txt:
--------------------------------------------------------------------------------
1 | $(dark_green, bold)As I see it, yes
2 | $(dark_green, bold)It is certain
3 | $(dark_green, bold)It is decidedly so
4 | $(dark_green, bold)Most likely
5 | $(dark_green, bold)Outlook good
6 | $(dark_green, bold)Signs point to yes
7 | $(dark_green, bold)One would be wise to think so
8 | $(dark_green, bold)Naturally
9 | $(dark_green, bold)Without a doubt
10 | $(dark_green, bold)Yes
11 | $(dark_green, bold)Yes, definitely
12 | $(dark_green, bold)You may rely on it
13 | $(bold)Reply hazy, try again
14 | $(bold)Ask again later
15 | $(bold)Better not tell you now
16 | $(bold)Cannot predict now
17 | $(bold)Concentrate and ask again
18 | $(bold)You know the answer better than I
19 | $(bold)Maybe...
20 | $(dark_red, bold)You're kidding, right?
21 | $(dark_red, bold)Don't count on it
22 | $(dark_red, bold)In your dreams
23 | $(dark_red, bold)My reply is no
24 | $(dark_red, bold)My sources say no
25 | $(dark_red, bold)Outlook not so good
26 | $(dark_red, bold)Very doubtful
27 |
--------------------------------------------------------------------------------
/data/attacks/bite.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "{action} {user}'s {bodypart}"
4 | ],
5 | "parts": {
6 | "action": [
7 | "bites",
8 | "nips",
9 | "nibbles",
10 | "chomps",
11 | "licks",
12 | "teases",
13 | "chews",
14 | "gums",
15 | "tastes"
16 | ],
17 | "bodypart": [
18 | "cheeks",
19 | "ear lobes",
20 | "nipples",
21 | "nose",
22 | "neck",
23 | "toes",
24 | "fingers",
25 | "butt",
26 | "taint",
27 | "thigh",
28 | "grundle",
29 | "tongue",
30 | "calf",
31 | "nurses",
32 | "nape"
33 | ]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/data/attacks/clinton.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "thinks {user} is {insult}",
4 | "thinks that {user} helped Clinton delete her emails",
5 | "knows that {user} assisted Clinton with Benghazi",
6 | "saw {user} help Clinton when she collapsed",
7 | "tells {user} to Pok\u00e9mon GO to the polls",
8 | "did not have sexual relations with {user}",
9 | "buys {user} a blue dress"
10 | ],
11 | "parts": {
12 | "insult": [
13 | "deplorable",
14 | "a basement dweller"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/data/attacks/compliment.json:
--------------------------------------------------------------------------------
1 | {
2 | "target_templates": [
3 | "{user}, {phrase}."
4 | ],
5 | "parts": {
6 | "phrase": [
7 | "you are like a spring flower; beautiful and vivacious",
8 | "you smell nice",
9 | "you have very nice teeth",
10 | "I like your pants",
11 | "you have a wonderful face",
12 | "you've got a nice butt",
13 | "I am utterly disarmed by your wit",
14 | "your beauty is why poetry was invented",
15 | "your eyes shine like the sun",
16 | "your skin is as soft as Froyo",
17 | "how does your head hold such a big brain"
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/data/attacks/fight.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "{bang}! {bang}! {bang}! {nick} {victory} over {user} with a {blow_type} {blow}.",
4 | "{bang}! {bang}! {bang}! {user} {victory} over {nick} with a {blow_type} {blow}."
5 | ],
6 | "parts": {
7 | "bang": [
8 | "BANG",
9 | "POW",
10 | "SLAM",
11 | "WHACK",
12 | "SLAP",
13 | "KAPOW",
14 | "ZAM",
15 | "BOOM"
16 | ],
17 | "blow_type": [
18 | "devastating",
19 | "destructive",
20 | "ruthless",
21 | "damaging",
22 | "ruinous",
23 | "catastrophic",
24 | "traumatic",
25 | "shattering",
26 | "overwhelming",
27 | "crushing",
28 | "fierce",
29 | "deadly",
30 | "lethal",
31 | "fatal",
32 | "savage",
33 | "violent"
34 | ],
35 | "victory": [
36 | "wins",
37 | "stands victorious",
38 | "triumphs",
39 | "conquers",
40 | "is the champion",
41 | "is the victor"
42 | ],
43 | "blow": [
44 | "uppercut",
45 | "hammerfist",
46 | "elbow strike",
47 | "shoulder strike",
48 | "front kick",
49 | "side kick",
50 | "roundhouse kick",
51 | "knee strike",
52 | "butt strike",
53 | "headbutt",
54 | "haymaker punch",
55 | "palm strike",
56 | "pocket bees"
57 | ]
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/data/attacks/glomp.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "{action} {user}."
4 | ],
5 | "parts": {
6 | "action": [
7 | "glomps",
8 | "tackles",
9 | "tackle hugs",
10 | "sexually glomps",
11 | "takes a flying leap and glomps",
12 | "bear hugs"
13 | ]
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/data/attacks/highfive.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "{nick} tries to give {user} a five up high but misses. that was awkward",
4 | "{nick} gives {user} a killer high-five",
5 | "{nick} gives {user} an elbow-shattering high-five",
6 | "{nick} smashes {user} up high",
7 | "{nick} slaps skin with {user}",
8 | "{nick} {user} winds up for a killer five but misses and falls flat on his face",
9 | "{nick} halfheartedly high-fives {user}",
10 | "{nick} gives {user} a smooth five down low",
11 | "{nick} gives {user} a friendly high five",
12 | "{nick} starts to give {user} a high five, but leaves them hanging",
13 | "{nick} performs an incomprehensible handshake with {user} that identifies them as the very best of friends",
14 | "{nick} makes as if to high five {user} but pulls his hand away at the last second",
15 | "{nick} leaves {user} hanging",
16 | "{nick} offers a fist and {user} pounds it"
17 | ],
18 | "parts": {}
19 | }
20 |
--------------------------------------------------------------------------------
/data/attacks/hug.json:
--------------------------------------------------------------------------------
1 | {
2 | "parts": {},
3 | "templates": [
4 | "{nick} wraps arms around {target} and clings forever",
5 | "{nick} gives {target} a BIIIIIIIIG hug!!!",
6 | "{nick} gives {target} a warming hug",
7 | "{nick} hugs {target} into a coma",
8 | "{nick} squeezes {target} to death",
9 | "{nick} gives {target} a christian side hug",
10 | "{nick} glomps {target}",
11 | "{nick} reluctantly hugs {target}...",
12 | "{nick} gives {target} a well-deserved hug :)",
13 | "{nick} hugs {target}",
14 | "{nick} hugs {target} forever and ever and ever",
15 | "cant stop, wont stop. {nick} hugs {target} until the sun goes cold",
16 | "{nick} rallies up everyone in the channel to give {target} a group hug",
17 | "{nick} gives {target} a tight hug and rubs their back",
18 | "{nick} hugs {target} and gives their hair a sniff",
19 | "{nick} smothers {target} with a loving hug"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/data/attacks/lurve.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "{nick} wraps arms around {target} and clings forever",
4 | "{nick} cuddles {target} in the fluffiest blanket ever",
5 | "{nick} lays their head on the lap of {target} and goes to sleep, dreaming da best sweet dreams",
6 | "{nick} caresses {target}'s hair",
7 | "{nick} caresses {target}'s cheek",
8 | "{nick} plants a shy kiss on {target}'s cheek",
9 | "{nick} gives {target} a BIIIIIIIIG hug!!!",
10 | "{nick} lovingly tackles {target} into a pit of the softest pillows ever",
11 | "{nick} cheers happily for {target}!!",
12 | "{nick} pulls {target} back into bed for more cuddles \u2665~",
13 | "{nick} snuggles {target} for Netflix and chili popcorn",
14 | "{nick} happily kisses {target} on the cheek",
15 | "{nick} shares a milkshake with {target}"
16 | ],
17 | "parts": {}
18 | }
19 |
--------------------------------------------------------------------------------
/data/attacks/present.json:
--------------------------------------------------------------------------------
1 | {
2 | "target_templates": [
3 | "hands {user} a {gift}"
4 | ],
5 | "parts": {
6 | "gift": [
7 | "Lighter",
8 | "VCR",
9 | "Video Game",
10 | "Blu-Ray Player",
11 | "Red Rider BB Gun",
12 | "Coal",
13 | "Chocolate",
14 | "Socks",
15 | "Necklace",
16 | "Watch",
17 | "20 dollars",
18 | "100 dollars",
19 | "50 dollars",
20 | "5 dollars",
21 | "lego technic set",
22 | "Makeup",
23 | "Gloves",
24 | "Tv",
25 | "PS4",
26 | "Amazon Gift Card",
27 | "Wii",
28 | "X-Box",
29 | "two front teeth",
30 | "DS",
31 | "Home Depot Gift Card",
32 | "Grocery store Gift Card",
33 | "Costco Gift Card",
34 | "Dollar Store Gift Card",
35 | "Mall Gift Card",
36 | "Spa Gift Card",
37 | "Computer",
38 | "Laptop",
39 | "Car",
40 | "Toaster",
41 | "Mixer",
42 | "BottleofBooze",
43 | "Beer",
44 | "Pen",
45 | "Notepad",
46 | "Dvd",
47 | "lotteryticket",
48 | "Blu-Ray"
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/data/attacks/spank.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "grabs {user} and {spank} them {xtimes} times with {item}."
4 | ],
5 | "parts": {
6 | "spank": [
7 | "paddles",
8 | "spanks",
9 | "whips",
10 | "lashes",
11 | "straps",
12 | "smacks",
13 | "wallops"
14 | ],
15 | "xtimes": [
16 | "two",
17 | "three",
18 | "four",
19 | "five",
20 | "six",
21 | "seven",
22 | "eight",
23 | "nine",
24 | "ten"
25 | ],
26 | "item": [
27 | "a riding crop",
28 | "a wooden spoon",
29 | "a leather flogger",
30 | "a wooden paddle",
31 | "a wooden cane",
32 | "a hickory tree switch",
33 | "a feather duster",
34 | "a cat o' nine tails",
35 | "a leather belt",
36 | "an open hand"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/data/attacks/strax.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "Might I suggest {type} {attack} with {preweapon} {postweapon} and {weapon}!"
4 | ],
5 | "target_templates": [
6 | "Might I suggest {type} {attack} on {user} with {preweapon} {postweapon} and {weapon}!"
7 | ],
8 | "parts": {
9 | "type": [
10 | "a full-frontal",
11 | "a pincer",
12 | "a surprise",
13 | "a brutally excessive",
14 | "a suicide",
15 | "a multi-pronged",
16 | "a glorious",
17 | "an acid-heavy",
18 | "an immediate",
19 | "a violent",
20 | "a traditional Sontaran",
21 | "a devasting"
22 | ],
23 | "attack": [
24 | "assault",
25 | "attack",
26 | "bombardment",
27 | "offensive",
28 | "barrage",
29 | "charge",
30 | "strike",
31 | "operation",
32 | "manoeuvre"
33 | ],
34 | "preweapon": [
35 | "laser",
36 | "berserker",
37 | "acid",
38 | "armoured attack",
39 | "proton",
40 | "three kinds of",
41 | "atomic",
42 | "toxic",
43 | "explosive",
44 | "red-hot",
45 | "thermal",
46 | "automated fire",
47 | "cluster",
48 | "enhanced germ",
49 | "energy-drink-fueled"
50 | ],
51 | "postweapon": [
52 | "bees",
53 | "chainsaws",
54 | "marmots",
55 | "acid",
56 | "monkeys",
57 | "mines",
58 | "bombs",
59 | "snakes",
60 | "spiders",
61 | "knives",
62 | "rockets",
63 | "sharks",
64 | "owls",
65 | "repurposed cybermats",
66 | "cannons",
67 | "alligators",
68 | "scalpel mines"
69 | ],
70 | "weapon": [
71 | "robots",
72 | "ninjas",
73 | "grenades",
74 | "a dolphin full of napalm",
75 | "acid",
76 | "dynamite",
77 | "xenomorphs",
78 | "lots and lots of C4",
79 | "tactical nukes",
80 | "MacGyver",
81 | "bio-weapons",
82 | "rocket launchers",
83 | "an elephant",
84 | "automated laser monkeys",
85 | "a memory worm for afterwards",
86 | "this pencil"
87 | ]
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/data/attacks/trump.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "grabs {user} by the pussy.",
4 | "walks in on {user} while undressing.",
5 | "pops a tic-tac and kisses {user}.",
6 | "calls {user} {insult}.",
7 | "makes the wall 10 feet higher and makes {user} pay for it.",
8 | "thinks {user} isn't sending their best.",
9 | "thinks {user} is great. the greatest. a really great user.",
10 | "thinks {user} has a yuge problem",
11 | "thinks people love {user}. And you know what, {user} has been very successful. Everybody loves {user}.",
12 | "thinks {user} actually doesn't have a bad hairline.",
13 | "makes {user} great again.",
14 | "knows that {user} had someone killed to keep them quiet.",
15 | "saw {user} rig the DNC primaries in favor of Clinton."
16 | ],
17 | "parts": {
18 | "insult": [
19 | "a huge disaster",
20 | "crooked",
21 | "a nasty woman",
22 | "a bad hombre",
23 | "dumb as a rock",
24 | "very sad",
25 | "a total hypocrite",
26 | "not a nice person",
27 | "a dope",
28 | "a total failure",
29 | "not big league"
30 | ]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/data/cheers.txt:
--------------------------------------------------------------------------------
1 | FUCK YEAH!
2 | HOORAH!
3 | HURRAY!
4 | OORAH!
5 | YAY!
6 | *\o/* CHEERS! *\o/*
7 | HOOHAH!
8 | HOOYAH!
9 | HUAH!
10 | ♪ ┏(°.°)┛ ┗(°.°)┓ ♬
11 |
--------------------------------------------------------------------------------
/data/foaas.json:
--------------------------------------------------------------------------------
1 | {
2 | "fuck_offs": [
3 | "donut",
4 | "bus",
5 | "chainsaw",
6 | "king",
7 | "madison",
8 | "gfy",
9 | "back",
10 | "keep",
11 | "name",
12 | "bday",
13 | "dalton",
14 | "ing",
15 | "nugget",
16 | "outside",
17 | "off",
18 | "problem",
19 | "shakespeare",
20 | "think",
21 | "thinking",
22 | "xmas",
23 | "yoda",
24 | "you"
25 | ],
26 | "single_fucks": [
27 | "bag",
28 | "awesome",
29 | "because",
30 | "bucket",
31 | "bye",
32 | "cool",
33 | "everyone",
34 | "everything",
35 | "flying",
36 | "give",
37 | "horse",
38 | "life",
39 | "looking",
40 | "maybe",
41 | "me",
42 | "mornin",
43 | "no",
44 | "pink",
45 | "retard",
46 | "rtfm",
47 | "sake",
48 | "shit",
49 | "single",
50 | "thanks",
51 | "that",
52 | "this",
53 | "too",
54 | "tucker",
55 | "zayn",
56 | "zero"
57 | ]
58 | }
59 |
--------------------------------------------------------------------------------
/data/food/cake.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "{method} {user} a {flavor} {size} {type} cake and serves it with a small {side}!"
4 | ],
5 | "parts": {
6 | "method": [
7 | "makes",
8 | "gives",
9 | "gets",
10 | "buys"
11 | ],
12 | "flavor": [
13 | "tasty",
14 | "delectable",
15 | "delicious",
16 | "yummy",
17 | "toothsome",
18 | "scrumptious",
19 | "luscious"
20 | ],
21 | "size": [
22 | "small",
23 | "little",
24 | "mid-sized",
25 | "medium-sized",
26 | "large",
27 | "gigantic"
28 | ],
29 | "type": [
30 | "Chocolate",
31 | "Ice Cream",
32 | "Angel",
33 | "Boston Cream",
34 | "Birthday",
35 | "Bundt",
36 | "Carrot",
37 | "Coffee",
38 | "Devils",
39 | "Fruit",
40 | "Gingerbread",
41 | "Pound",
42 | "Red Velvet",
43 | "Stack",
44 | "Welsh",
45 | "Yokan"
46 | ],
47 | "side": [
48 | "glass of chocolate milk",
49 | "bowl of ice cream",
50 | "jar of cookies",
51 | "side of chocolate sauce"
52 | ]
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/data/food/cheese.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a {size} {type} of {cheese}."
4 | ],
5 | "parts": {
6 | "size": [
7 | "small",
8 | "medium",
9 | "large",
10 | "double",
11 | "half-eaten",
12 | "semi-chewed on",
13 | "generously big",
14 | "enormous",
15 | "comically tiny",
16 | "stolen",
17 | "smuggled",
18 | "confiscated",
19 | "rare",
20 | "half off at the market",
21 | "Deli's special"
22 | ],
23 | "type": [
24 | "slice",
25 | "serving",
26 | "slab",
27 | "wheel",
28 | "crate",
29 | "crumb",
30 | "morsel",
31 | "brick",
32 | "ball",
33 | "tube",
34 | "tub",
35 | "jar",
36 | "bath tub",
37 | "warehouse",
38 | "freight car"
39 | ],
40 | "cheese": [
41 | "KoKo\u2019s coconut gouda",
42 | "Chocolate stout cheddar",
43 | "White stilton with mango and ginger",
44 | "Baked brie with jam",
45 | "Baked brie with roasted garlic and walnuts",
46 | "Lambchopper",
47 | "Smokey goat cheese",
48 | "Velveeta",
49 | "Smoked halloumi",
50 | "Aged Gruyere",
51 | "Camembert De Normandie",
52 | "Leerdamer",
53 | "Pesto Gouda",
54 | "Pepper Jack cheese",
55 | "Cranberry cheddar",
56 | "Mozzarella",
57 | "Feta cheese",
58 | "Stilton",
59 | "Bavarian smoked cheese",
60 | "Veiny gorgonzola",
61 | "Dill havarti",
62 | "Tillamook pepper jack cheese",
63 | "Drunken gouda",
64 | "Drunken goat",
65 | "Habanero cheddar",
66 | "Red Leicester",
67 | "Grana-Pandano",
68 | "Emmentaler",
69 | "Camembert",
70 | "Provolone ",
71 | "Gruyere",
72 | "Parmesan cheese ",
73 | "Roquefort cheese",
74 | "Sharp cheddar",
75 | "Raclette cheese",
76 | "Mortadella cheese",
77 | "Ricotta",
78 | "Mascarpone",
79 | "Cream Cheese",
80 | "Taleggio",
81 | "head cheese"
82 | ]
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/data/food/coffee.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a {size} {temperature} {flavor} {coffee} with {shots} of espresso!"
4 | ],
5 | "parts": {
6 | "temperature": [
7 | "hot",
8 | "cold",
9 | "iced",
10 | "room temperature",
11 | "lukewarm"
12 | ],
13 | "size": [
14 | "large",
15 | "medium",
16 | "small",
17 | "grande",
18 | "venti",
19 | "tall"
20 | ],
21 | "flavor": [
22 | "vanilla",
23 | "caramel",
24 | "white chocolate",
25 | "hazelnut",
26 | "cinnamon"
27 | ],
28 | "coffee": [
29 | "americano",
30 | "cubano",
31 | "cappuccino",
32 | "latte",
33 | "mocha",
34 | "macchiato",
35 | "breve"
36 | ],
37 | "shots": [
38 | "one shot",
39 | "two shots",
40 | "three shots"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/data/food/cookies.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "{method} {user} a {flavor} {size} {type} cookie and serves it with a {side}!"
4 | ],
5 | "parts": {
6 | "method": [
7 | "makes",
8 | "gives",
9 | "gets",
10 | "buys"
11 | ],
12 | "flavor": [
13 | "tasty",
14 | "delectable",
15 | "delicious",
16 | "yummy",
17 | "toothsome",
18 | "scrumptious",
19 | "luscious"
20 | ],
21 | "size": [
22 | "small",
23 | "little",
24 | "medium-sized",
25 | "large",
26 | "gigantic"
27 | ],
28 | "type": [
29 | "Chocolate Chip",
30 | "Oatmeal",
31 | "Sugar",
32 | "Oatmeal Raisin",
33 | "Macadamia Nut",
34 | "Jam Thumbprint",
35 | "Mexican Wedding",
36 | "Biscotti",
37 | "Oatmeal Cranberry",
38 | "Chocolate Fudge",
39 | "Peanut Butter",
40 | "Pumpkin",
41 | "Lemon Bar",
42 | "Chocolate Oatmeal Fudge",
43 | "Toffee Peanut",
44 | "Danish Sugar",
45 | "Tim Tam",
46 | "Triple Chocolate",
47 | "Oreo"
48 | ],
49 | "side": [
50 | "glass of milk",
51 | "bowl of ice cream",
52 | "bowl of chocolate sauce"
53 | ]
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/data/food/donut.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a {donut} donut."
4 | ],
5 | "parts": {
6 | "donut": [
7 | "chocolate long john",
8 | "vanilla long john",
9 | "bear claw",
10 | "old fashioned",
11 | "Boston cream",
12 | "jelly",
13 | "chocolate cake",
14 | "glazed",
15 | "strawberry frosted with sprinkles",
16 | "chocolate frosted with sprinkles",
17 | "vanilla frosted with sprinkles",
18 | "blueberry cake",
19 | "cinnamon",
20 | "sugared",
21 | "assorted holes",
22 | "powdered",
23 | "apple fritter",
24 | "double chocolate",
25 | "maple glaze"
26 | ]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/data/food/doobie.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "passes {user} a {size} {adjective} {doobie} made with the finest {strain}"
4 | ],
5 | "parts": {
6 | "adjective": [
7 | "perfectly-rolled",
8 | "sweet-smelling",
9 | "dank, dank",
10 | "killer",
11 | "cherried",
12 | "world record breaking",
13 | "glorious",
14 | "unfathomable",
15 | "awe-inspiring",
16 | "spine-chilling"
17 | ],
18 | "size": [
19 | "footlong",
20 | "medium",
21 | "snoopworthy",
22 | "pathetically-sized",
23 | "one ounce",
24 | "regular-sized",
25 | "giant",
26 | "super size",
27 | "king size",
28 | "1 and 1/4"
29 | ],
30 | "doobie": [
31 | "doobie",
32 | "spliff",
33 | "joint",
34 | "blunt",
35 | "reefer"
36 | ],
37 | "strain": [
38 | "Blue Dream",
39 | "Sour Diesel",
40 | "OG Kush",
41 | "Shitty UK Cheese",
42 | "Grandy Purp",
43 | "White Widow",
44 | "Pineapple Express",
45 | "Girl Scout Cookies",
46 | "Strawberry Banana",
47 | "Dutch Treat",
48 | "BlueBerry Muffin",
49 | "Green Crack",
50 | "Sour Diesel",
51 | "Paris OG",
52 | "Tangie",
53 | "Sour Tangie",
54 | "BlueBerry Headband",
55 | "Larry Bird OG",
56 | "Pineapple Chunk",
57 | "Gorilla Glue #4",
58 | "Durban Poision"
59 | ]
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/data/food/halal.json:
--------------------------------------------------------------------------------
1 | {
2 | "target_templates": [
3 | "Serves {target} {quantity} {quality} {dish}"
4 | ],
5 | "templates": [
6 | "has {quantity} {quality} {dish}"
7 | ],
8 | "parts": {
9 | "quantity": [
10 | "a little bit of",
11 | "a heaping pile of",
12 | "a moderate serving of",
13 | "a taste of",
14 | "just a smell of"
15 | ],
16 | "quality": [
17 | "fresh made",
18 | "left over",
19 | "just out of the oven"
20 | ],
21 | "dish": [
22 | "Rice and Goat Meat",
23 | "Goat Curry",
24 | "Hummus bi Tahina",
25 | "L\u00e4ghm\u00e4n",
26 | "Mutton biryani",
27 | "Kabuli palao",
28 | "Shakshouka",
29 | "Mutton Msala",
30 | "Fatteh Betnjan",
31 | "Caprese stuffed chicken breast",
32 | "Maqloobeh",
33 | "Koofteh berenji",
34 | "Fish Makkanwala",
35 | "Szechwan"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/data/food/kebab.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a delicious {kebab} with {side} and {sauce}"
4 | ],
5 | "parts": {
6 | "kebab": [
7 | "lamb doner kebab",
8 | "chicken doner kebab",
9 | "shawarma"
10 | ],
11 | "side": [
12 | "salad",
13 | "fries"
14 | ],
15 | "sauce": [
16 | "garlic sauce",
17 | "chili sauce",
18 | "mayonnaise",
19 | "ketchup",
20 | "BBQ sauce",
21 | "mint sauce",
22 | "tzatziki sauce",
23 | "no sauce"
24 | ]
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/data/food/keto.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a {meal}."
4 | ],
5 | "parts": {
6 | "meal": [
7 | "bacon, red pepper, and mozzarella frittatta",
8 | "crockpot buffalo chicken soup",
9 | "ginger sesame glazed salmon",
10 | "egg salad stuffed avocado",
11 | "bacon cheeseburger soup",
12 | "pork carne asada taco",
13 | "stuffed poblano pepper",
14 | "keto coffee cake",
15 | "almond butter chia square",
16 | "caprese salad",
17 | "bacon wrapped asparagus",
18 | "Breakfast keto waffles",
19 | "Jalapeno popper egg cups",
20 | "Bacon cheddar chive omelette",
21 | "Mini keto pancake donuts",
22 | "Avocado tuna melt bites",
23 | "Cheese stuffed bacon wrapped hot dogs",
24 | "5 minute keto egg drop soup",
25 | "Crispy tofu and bok choy salad",
26 | "Keto coconut curry chicken tenders",
27 | "Asian grilled keto short ribs",
28 | "Cheese stuffed bacon cheeseburger",
29 | "Perfectly crisp baked chicken wings",
30 | "No bake chocolate peanut butter fat bombs",
31 | "Keto tortilla chips",
32 | "Neapolitan fat bombs",
33 | "Coconut orange creamsicle fat bombs",
34 | "Reverse seared ribeye steak",
35 | "Cauliflower base pepperoni pizza",
36 | "Bacon and bacon and bacon and more bacon"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/data/food/muffin.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a {muffin} muffin."
4 | ],
5 | "parts": {
6 | "muffin": [
7 | "blueberry",
8 | "lemon poppyseed",
9 | "chocolate",
10 | "chocolate chip",
11 | "orange cranberry zest",
12 | "corn",
13 | "pumpkin",
14 | "banana nut",
15 | "apple cinnamon"
16 | ]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/data/food/noodles.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a {noodle} served with {side} and {side}"
4 | ],
5 | "parts": {
6 | "noodle": [
7 | "spicy bowl of mie goreng fried noodles",
8 | "hot bowl of pho noodle soup",
9 | "bowl of pork and mushroom wonton noodle soup",
10 | "plate of pan fried rice noodles with sliced beef",
11 | "plate of glass noodles with seasonal vegetables",
12 | "bowl of vegetable yakisoba",
13 | "bowl of vermicelli noodles with lemongrass chicken and spicy fish sauce",
14 | "plate of pancit luglug topped with hardboiled eggs, shrimp, and chorizo",
15 | "bowl of pancit malabon with freshly caught seafood",
16 | "hot bowl of chicken noodle soup",
17 | "extra large bowl of saimin with cabbage",
18 | "whole wok of Singapore-style noodles with extra curry flavor",
19 | "plate of mildly spicy drunken noodles",
20 | "steaming bowl of dandan noodles topped with peanut butter",
21 | "takeout box of Shanghai-style noodles with extra choi",
22 | "generous bowl of b\u00fan b\u00f2 hu\u1ebf",
23 | "hot bowl of curry laksa with tofu puffs and cuttlefish",
24 | "large portion of bibim guksu",
25 | "steaming bowl of tonkatsu ramen",
26 | "pot of shirataki noodles",
27 | "small bowl of s\u014dmen salad",
28 | "plate of classic spaghetti with meatballs"
29 | ],
30 | "side": [
31 | "a poached egg",
32 | "a sunny side up egg",
33 | "shredded chicken",
34 | "shredded pork",
35 | "a pork cutlet",
36 | "packaged crackers",
37 | "pickled vegetables",
38 | "some grilled lemongrass-rubbed fish",
39 | "roasted seaweed",
40 | "panko-crusted deep fried prawns",
41 | "extra garlicky toasted bread",
42 | "a side of kimchi",
43 | "deep fried tofu",
44 | "a side of sliced beef brisket",
45 | "a side of peppery fried chicken nuggets",
46 | "a side of spring rolls",
47 | "a side of salad rolls",
48 | "a side of gyoza dumplings"
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/data/food/rice.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a bowl of {rice} rice."
4 | ],
5 | "parts": {
6 | "rice": [
7 | "Uncle Ben's Basmati",
8 | "Uncle Ben's Golden Vegetable ",
9 | "Japanese Koshihikari",
10 | "Camargue red",
11 | "Brown",
12 | "Wehani",
13 | "Jasmine",
14 | "Sticky",
15 | "Hokkien fried",
16 | "Bibimbap",
17 | "Dal bhat",
18 | "Onigiri",
19 | "Long-grain Nychaki",
20 | "Iranian Domsiah",
21 | "Italian Vialone Nano",
22 | "N\u00e0ng Th\u01a1m Ch\u1ee3 \u0110\u00e0o",
23 | "Htaman\u00e8",
24 | "Aromic Tulaipanji"
25 | ]
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/data/food/sandwich.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "{gives} {user} {flavor} {meat} and {meat} {type} with {salad} and {sauce}!",
4 | "{gives} {user} {flavor} {meat} and {meat} {type} with {salad}, {salad}, and {sauce}!",
5 | "{gives} {user} {flavor} {meat} and {meat} {type} with {salad}, {salad}, {salad}, and {sauce}!",
6 | "{gives} {user} {flavor} {meat} {type} with {salad} and {sauce}!",
7 | "{gives} {user} {flavor} {meat} {type} with {salad}, {salad}, and {sauce}!",
8 | "{gives} {user} {flavor} {meat} {type} with {salad}, {salad}, {salad}, and {sauce}!"
9 | ],
10 | "parts": {
11 | "gives": [
12 | "hands",
13 | "gives",
14 | "makes",
15 | "passes"
16 | ],
17 | "flavor": [
18 | "a tasty",
19 | "a delicious",
20 | "an awesome",
21 | "an excellent",
22 | "a beautifully-made"
23 | ],
24 | "meat": [
25 | "ham",
26 | "salami",
27 | "steak",
28 | "meatball",
29 | "tuna",
30 | "pork",
31 | "chicken",
32 | "roast beef",
33 | "corned beef",
34 | "pastrami",
35 | "turkey"
36 | ],
37 | "type": [
38 | "roll",
39 | "wrap",
40 | "pita",
41 | "sandwich",
42 | "bun"
43 | ],
44 | "salad": [
45 | "tomatoes",
46 | "lettuce",
47 | "pickles",
48 | "cucumbers",
49 | "red onions",
50 | "jalapenos"
51 | ],
52 | "sauce": [
53 | "sweet onion sauce",
54 | "honey mustard sauce",
55 | "mayo",
56 | "sweet chili sauce",
57 | "italian sauce",
58 | "ranch dressing",
59 | "barbecue sauce"
60 | ]
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/data/food/scone.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a {scone} scone."
4 | ],
5 | "parts": {
6 | "scone": [
7 | "almond",
8 | "apple brie",
9 | "apricot ",
10 | "blueberry",
11 | "blackberry",
12 | "pumpkin",
13 | "chocolate and orange",
14 | "chocolate",
15 | "cinnamon sugar",
16 | "coconut",
17 | "cranberry",
18 | "cranberry lime",
19 | "lemon glazed",
20 | "maple bacon brown sugar",
21 | "Nutella",
22 | "pear and goat cheese",
23 | "strawberry",
24 | "cheddar and roasted red pepper",
25 | "lemon poppyseed",
26 | "vanilla bean",
27 | "cherry almond",
28 | "pesto and sun-dried tomato",
29 | "raspberry",
30 | "lavender and honey",
31 | "orange and cranberry",
32 | "oatmeal pecan",
33 | "cheddar and rosemary",
34 | "bacon and gruyere",
35 | "black currant",
36 | "potato"
37 | ]
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/data/food/soup.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a {size} {soup} soup with {side}."
4 | ],
5 | "parts": {
6 | "soup": [
7 | "Ajiaco",
8 | "Avgolemono",
9 | "Borscht",
10 | "Beef noodle",
11 | "Beer",
12 | "Birds nest",
13 | "Chicken noodle",
14 | "Caldo verde",
15 | "Cazuela",
16 | "Chicken Noodle",
17 | "Cock-a-leekie",
18 | "Cream of crab",
19 | "Fufu and Egusi",
20 | "Gomguk",
21 | "Goulash",
22 | "Gumbo",
23 | "Kharcho",
24 | "Kimchi Guk",
25 | "Lagman",
26 | "Leek",
27 | "Lentil",
28 | "Maryland crab",
29 | "Matzah ball",
30 | "Menudo",
31 | "Minestrone",
32 | "Miyeok guk",
33 | "Milligatawny",
34 | "Barley",
35 | "Nettle",
36 | "Oxtail",
37 | "Pozole",
38 | "Pumpkin",
39 | "Samgyetang",
40 | "Snert",
41 | "Corn chowder",
42 | "French onion",
43 | "Lobster",
44 | "Miso",
45 | "She-crab",
46 | "Tomato",
47 | "Tteokguk",
48 | "Winter mellon",
49 | "Crab Gaxpacho",
50 | "Salmorejo",
51 | "Tarator",
52 | "New England clam chowder"
53 | ],
54 | "size": [
55 | "Large bowl of",
56 | "Small bowl of",
57 | "Cup of",
58 | "Gallon of",
59 | "Medium bowl of",
60 | "Bread bowl of"
61 | ],
62 | "side": [
63 | "Some Croutons",
64 | "A baguette",
65 | "Sliced apples",
66 | "Chicken"
67 | ]
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/data/food/sushi.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "gives {user} a {sushi} roll {sushi_extra}"
4 | ],
5 | "parts": {
6 | "sushi": [
7 | "Kappa Maki",
8 | "California",
9 | "Philadelphia",
10 | "Hamachi Yellow Tail",
11 | "Tamagoyaki fried egg",
12 | "Futomaki",
13 | "Natto Maki Soy Bean",
14 | "Negitoro Blue Fin",
15 | "Kamaboko Kani Crab",
16 | "Philadelphia",
17 | "Crab meat",
18 | "Raw salmon",
19 | "Raw tuna",
20 | "Eel",
21 | "Seared salmon",
22 | "Seared Ahi Tuna",
23 | "Shark",
24 | "Shrimp tempura",
25 | "Dragon"
26 | ],
27 | "sushi_extra": [
28 | "drizzled with eel sauce",
29 | "garnished with lemon",
30 | "decorated with cilantro",
31 | "topped with a line of sriracha aeoli",
32 | "prepared with love",
33 | "made to the standards of the first emperor of Japan",
34 | "rolled to perfection",
35 | "with a bit of wasabi",
36 | "decorated with little cucumber slices",
37 | "so good master banishes him from his temple",
38 | "so amazing the emperor appoints him as Chief Sushi Chef",
39 | "forged of iron!",
40 | "with the freshest seaweed off the coasts of Japan",
41 | "delicately crafted with mastery",
42 | "that shows the true skill of the sushi chef",
43 | "with flavor subtle like the ninja warriors of Kyoto"
44 | ]
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/data/food/taco.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a {quality} {type} taco filled with {meat} and topped with {topping}, {topping} and {topping}!"
4 | ],
5 | "parts": {
6 | "type": [
7 | "hard-shell",
8 | "soft-shell",
9 | [
10 | "crispy",
11 | 1
12 | ],
13 | [
14 | "puffy",
15 | 1
16 | ],
17 | [
18 | "Indian",
19 | 1
20 | ]
21 | ],
22 | "quality": [
23 | "spicy",
24 | "mild",
25 | "delicious",
26 | [
27 | "boring",
28 | 1
29 | ],
30 | [
31 | "disgusting",
32 | 1
33 | ],
34 | "perfect"
35 | ],
36 | "meat": [
37 | "minced beef",
38 | "shredded beef",
39 | "steak",
40 | "pork",
41 | "various meats",
42 | "chicken",
43 | "refried beans",
44 | [
45 | "tofu",
46 | 1
47 | ]
48 | ],
49 | "topping": [
50 | [
51 | "guacamole",
52 | 10
53 | ],
54 | [
55 | "salsa",
56 | 10
57 | ],
58 | [
59 | "sour cream",
60 | 10
61 | ],
62 | "cheese",
63 | "lettuce",
64 | "tomatoes",
65 | "avocado",
66 | "onion",
67 | "scallions",
68 | "jalape\u00f1os",
69 | "capsicum",
70 | [
71 | "ghost chili",
72 | 1
73 | ],
74 | [
75 | "olives",
76 | 1
77 | ],
78 | [
79 | "pineapple",
80 | 1
81 | ],
82 | [
83 | "raspberries",
84 | 1
85 | ]
86 | ]
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/data/food/tea.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "hands {user} a {size} {temperature} {tea} tea!"
4 | ],
5 | "parts": {
6 | "temperature": [
7 | "hot",
8 | "room temperature",
9 | "lukewarm",
10 | "steaming"
11 | ],
12 | "size": [
13 | "large",
14 | "medium",
15 | "small"
16 | ],
17 | "tea": [
18 | "chai",
19 | "english breakfast",
20 | "Earl Grey",
21 | "sleepytime",
22 | "oolong",
23 | "green",
24 | "yerba mate",
25 | "rooibos",
26 | "bubble",
27 | "thai"
28 | ]
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/data/hookup.json:
--------------------------------------------------------------------------------
1 | {
2 | "templates": [
3 | "{user1} used {weapon} and did it with {user2} in the {room}."
4 | ],
5 | "parts": {
6 | "weapon": [
7 | "a candlestick",
8 | "an axe",
9 | "a pistol",
10 | "rope",
11 | "gloves",
12 | "a horseshoe",
13 | "a knife",
14 | "a baseball bat",
15 | "a chalice",
16 | "a dumbbell",
17 | "a wrench",
18 | "a trophy",
19 | "a pipe",
20 | "garden shears"
21 | ],
22 | "room": [
23 | "courtyard",
24 | "guest house",
25 | "observatory",
26 | "theatre",
27 | "drawing room",
28 | "garage",
29 | "spa",
30 | "master bedroom",
31 | "studio",
32 | "pool",
33 | "arcade",
34 | "beach house",
35 | "surf shop",
36 | "kitchen",
37 | "ballroom",
38 | "conservatory",
39 | "billiard room",
40 | "library",
41 | "study",
42 | "hallway",
43 | "lounge",
44 | "dining room",
45 | "cellar"
46 | ]
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/data/lenny.json:
--------------------------------------------------------------------------------
1 | {
2 | "lenny": [
3 | "( \u0361\u00b0 \u035c\u0296 \u0361\u00b0)",
4 | "( \u0360\u00b0 \u035f\u0296 \u0361\u00b0)",
5 | "\u1566( \u0361\u00b0 \u035c\u0296 \u0361\u00b0)\u1564",
6 | "( \u0361\u00b0 \u035c\u0296 \u0361\u00b0)",
7 | "( \u0361~ \u035c\u0296 \u0361\u00b0)",
8 | "( \u0361o \u035c\u0296 \u0361o)",
9 | "\u0361\u00b0 \u035c\u0296 \u0361 -",
10 | "( \u0361\u0361 \u00b0 \u035c \u0296 \u0361 \u00b0)\ufeff",
11 | "( \u0361 \u0361\u00b0 \u0361\u00b0 \u0296 \u0361\u00b0 \u0361\u00b0)",
12 | "(\u0e07 \u0360\u00b0 \u035f\u0644\u035c \u0361\u00b0)\u0e07",
13 | "( \u0361\u00b0 \u035c\u0296 \u0361 \u00b0)",
14 | "( \u0361\u00b0\u256d\u035c\u0296\u256e\u0361\u00b0 )"
15 | ],
16 | "flenny": [
17 | "( \u0361\u00b0 \u035c \u0361\u00b0 )",
18 | "( \u0361\u00b0 \u035c \u0361\u00b0 )",
19 | "(\u0e07 \u0360\u00b0 \u035f \u0361\u00b0 )\u0e07",
20 | "( \u0361\u00b0_ \u0361\u00b0 )",
21 | "(\ufffd \u0361\u00b0 \u035c \u0361\u00b0 )\ufffd",
22 | "( \u25d5 \u035c \u25d5 )",
23 | "( \u0361~ \u035c \u0361\u00b0 )",
24 | "( \u0360\u00b0 \u035f \u0361\u00b0 )",
25 | "( \u0ca0 \u035c \u0ca0 )",
26 | "( \u0ca5 \u035c \u0ca5 )",
27 | "( \u0361^ \u035c \u0361^ )",
28 | "( \u0ca5 _ \u0ca5 )",
29 | "( \u0361\u00b0 \uff0d \u0361\u00b0 )",
30 | "\u2570( \u0361\u00b0 \u035c \u0361\u00b0)\u2283\u2501\u2606\u309c\u30fb\u3002\u3002\u30fb\u309c\u309c\u30fb\u3002\u3002\u30fb\u309c\u2606\u309c\u30fb\u3002\u3002\u30fb\u309c\u309c\u30fb\u3002\u3002\u30fb\u309c",
31 | "\u2534\u252c\u2534\u252c\u2534\u2524( \u0361\u00b0 \u035c \u251c\u252c\u2534\u252c\u2534\u252c",
32 | "( \u2310\u25a0 \u035c \u25a0 )",
33 | "( \u0361~ _ \u0361~ )",
34 | "@=( \u0361\u00b0 \u035c \u0361\u00b0 @ )\u2261",
35 | "( \u0361\u00b0\u06a1 \u0361\u00b0 )",
36 | "( \u2716_\u2716 )",
37 | "(\u3065 \u0361\u00b0 \u035c \u0361\u00b0 )\u3065",
38 | "\u10da( \u0361\u00b0 \u035c \u0361\u00b0 \u10da)",
39 | "( \u25c9 \u035c \u0361\u25d4 )"
40 | ]
41 | }
42 |
--------------------------------------------------------------------------------
/data/name_files/dwarves.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Dwarven names",
3 | "author": "Johan Danforth",
4 | "templates": {
5 | "default": "{first}{mid}{final}"
6 | },
7 | "default_templates": [
8 | "default"
9 | ],
10 | "parts": {
11 | "final": [
12 | "bur",
13 | "fur",
14 | "gan",
15 | "gnus",
16 | "gnar",
17 | "li",
18 | "lin",
19 | "lir",
20 | "mli",
21 | "nar",
22 | "nus",
23 | "rin",
24 | "ran",
25 | "sin",
26 | "sil",
27 | "sur"
28 | ],
29 | "mid": [
30 | "a",
31 | "e",
32 | "i",
33 | "o",
34 | "oi",
35 | "u"
36 | ],
37 | "first": [
38 | "B",
39 | "D",
40 | "F",
41 | "G",
42 | "Gl",
43 | "H",
44 | "K",
45 | "L",
46 | "M",
47 | "N",
48 | "R",
49 | "S",
50 | "T",
51 | "V"
52 | ]
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/data/name_files/elves_female.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Elven female names",
3 | "author": "Johan Danforth",
4 | "templates": {
5 | "default": "{first}{mid}{final}"
6 | },
7 | "default_templates": [
8 | "default"
9 | ],
10 | "parts": {
11 | "final": [
12 | "clya",
13 | "lindi",
14 | "di",
15 | "dien",
16 | "dith",
17 | "dia",
18 | "lith",
19 | "lia",
20 | "ndra",
21 | "ng",
22 | "nia",
23 | "niel",
24 | "rith",
25 | "thien",
26 | "thiel",
27 | "viel",
28 | "wen",
29 | "wien",
30 | "wiel"
31 | ],
32 | "mid": [
33 | "a",
34 | "a",
35 | "adrie",
36 | "ara",
37 | "e",
38 | "e",
39 | "ebri",
40 | "i",
41 | "io",
42 | "ithra",
43 | "ilma",
44 | "il-Ga",
45 | "o",
46 | "orfi",
47 | "o",
48 | "u",
49 | "y"
50 | ],
51 | "first": [
52 | "An",
53 | "Am",
54 | "Bel",
55 | "Cel",
56 | "C",
57 | "Cal",
58 | "Del",
59 | "El",
60 | "Elr",
61 | "Elv",
62 | "Eow",
63 | "Ear",
64 | "F",
65 | "G",
66 | "Gal",
67 | "Gl",
68 | "H",
69 | "Is",
70 | "Leg",
71 | "Lem",
72 | "M",
73 | "N",
74 | "P",
75 | "R",
76 | "S",
77 | "T",
78 | "Thr",
79 | "Tin",
80 | "Ur",
81 | "Un",
82 | "V"
83 | ]
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/data/name_files/elves_male.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Elven male names",
3 | "author": "Johan Danforth",
4 | "templates": {
5 | "default": "{first}{mid}{final}"
6 | },
7 | "default_templates": [
8 | "default"
9 | ],
10 | "parts": {
11 | "final": [
12 | "l",
13 | "las",
14 | "lad",
15 | "ldor",
16 | "ldur",
17 | "lith",
18 | "mir",
19 | "n",
20 | "nd",
21 | "ndel",
22 | "ndil",
23 | "ndir",
24 | "nduil",
25 | "ng",
26 | "mbor",
27 | "r",
28 | "ril",
29 | "riand",
30 | "rion",
31 | "wyn"
32 | ],
33 | "mid": [
34 | "a",
35 | "a",
36 | "adrie",
37 | "ara",
38 | "e",
39 | "e",
40 | "ebri",
41 | "i",
42 | "io",
43 | "ithra",
44 | "ilma",
45 | "il-Ga",
46 | "o",
47 | "orfi",
48 | "o",
49 | "u",
50 | "y"
51 | ],
52 | "first": [
53 | "An",
54 | "Am",
55 | "Bel",
56 | "Cel",
57 | "C",
58 | "Cal",
59 | "Del",
60 | "El",
61 | "Elr",
62 | "Elv",
63 | "Eow",
64 | "Ear",
65 | "F",
66 | "G",
67 | "Gal",
68 | "Gl",
69 | "H",
70 | "Is",
71 | "Leg",
72 | "Lem",
73 | "M",
74 | "N",
75 | "P",
76 | "R",
77 | "S",
78 | "T",
79 | "Thr",
80 | "Tin",
81 | "Ur",
82 | "Un",
83 | "V"
84 | ]
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/data/name_files/hobbits.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Tolkien hobbit names",
3 | "author": "Johan Danforth",
4 | "templates": {
5 | "default": "{first}{mid}{final}"
6 | },
7 | "default_templates": [
8 | "default"
9 | ],
10 | "parts": {
11 | "final": [
12 | "bo",
13 | "do",
14 | "doc",
15 | "go",
16 | "grin",
17 | "m"
18 | ],
19 | "mid": [
20 | "a",
21 | "e",
22 | "i",
23 | "ia",
24 | "o",
25 | "oi",
26 | "u"
27 | ],
28 | "first": [
29 | "B",
30 | "Dr",
31 | "Fr",
32 | "Mer",
33 | "Per",
34 | "S"
35 | ]
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/data/name_files/narn.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Babylon 5 Narn names",
3 | "author": "Kevin G. Nunn",
4 | "templates": {
5 | "default": "{first}{mid}{final}"
6 | },
7 | "default_templates": [
8 | "default"
9 | ],
10 | "parts": {
11 | "final": [
12 | "ch",
13 | "k",
14 | "kk",
15 | "l",
16 | "n",
17 | "r",
18 | "th",
19 | "s"
20 | ],
21 | "mid": [
22 | "Ba",
23 | "Bo",
24 | "Da",
25 | "Do",
26 | "Ga",
27 | "Ge",
28 | "Go",
29 | "Ka",
30 | "Ko",
31 | "La",
32 | "Le",
33 | "Lo",
34 | "Ma",
35 | "Mo",
36 | "Na",
37 | "No",
38 | "Oo",
39 | "Pa",
40 | "Po",
41 | "Qua",
42 | "Quo",
43 | "Ra",
44 | "Rala",
45 | "Ro",
46 | "Sha",
47 | "Shali",
48 | "Ska",
49 | "Skali",
50 | "Sta",
51 | "Ste",
52 | "Sto",
53 | "Ta",
54 | "Te",
55 | "Tee",
56 | "To",
57 | "Tha",
58 | "Tho",
59 | "Va",
60 | "Vo",
61 | "Vy",
62 | "Wa"
63 | ],
64 | "first": [
65 | "Ch'",
66 | "Do'",
67 | "G'",
68 | "Gre'",
69 | "Mak'",
70 | "Na'",
71 | "Re'",
72 | "Sh'",
73 | "So'",
74 | "T'",
75 | "Ta'",
76 | "Th'",
77 | "Thu'",
78 | "Tu'"
79 | ]
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/data/wisdom.txt:
--------------------------------------------------------------------------------
1 | The best way to a man's heart is to saw his breast plate open. ~Women's restroom, Murphy's, Champaign, Ill.
2 | Beauty is only a light switch away. ~Perkins Library, Duke University, Durham, N.C.
3 | I've decided that to raise my grades, I must lower my standards. ~Houghton Library, Harvard University, Cambridge, Mass.
4 | If Bush were captain of the Titanic, he'd say we were stopping for ice. ~Smoky Joe's, Philadelphia, Penna.
5 | Remember, it's not "How high are you?" it's "Hi, how are you?". ~Rest stop off Route 81, W. Va.
6 | God made pot. Man made beer. Who do you trust? ~The Irish Times, Washington, D.C
7 | Fighting for peace is like screwing for virginity. ~The Bayou, Baton Rouge, La.
8 | No matter how good she looks, some other guy is sick and tired of putting up with her shit. ~Men's restroom, Linda's Bar and Grill, Chapel Hill, N.C.
9 | To do is to be. (Descartes) To be is to do. (Voltaire) Do be do be do. (Frank Sinatra) ~Men's restroom, Greasewood Flats, Scottsdale, Ariz.
10 | At the feast of ego, everyone leaves hungry. ~Bentley's House of Coffee and Tea, Tucson, Ariz.
11 | It's hard to make a comeback when you haven't been anywhere. ~Written in the dust on the back of a bus, Wickenburg, Ariz.
12 | Make love, not war. Hell, do both - get married! ~Women's restroom, The Filling Station, Bozeman, Mont.
13 | If voting could really change things, it would be illegal. ~Revolution Books, New York, N.Y.
14 | A woman's rule of thumb: If it has tires or testicles, you're going to have trouble with it. ~Women's restroom, Dick's Last Resort, Dallas, Tex.
15 | JESUS SAVES! But wouldn't it be better if he had invested? ~Men's restroom, American University, Washington, D.C.
16 | If pro is the opposite of con, then what is the opposite of progress? Congress! ~Men's restroom, House of Representatives, Washington, D.C.
17 | Express Lane: Five beers or less. Sign over one of the urinals. ~Ed Debevic's, Phoenix, Ariz.
18 | You're too good for him. ~Sign over mirror in women's restroom, Ed Debevic's, Beverly Hills,Calif.
19 | No wonder you always go home alone. Sign over mirror in men's restroom. ~Ed Debevic's, Beverly Hills, California
20 | What are you looking up on the wall for? The joke is in your hands. ~Men's restroom, Lynagh's, Lexington, Ky.
21 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # CloudBot Docs
2 |
3 | Welcome to CloudBot! This set of documentation is split into three folders.
4 |
5 | ### Folder Structure
6 |
7 | - User: The users folder holds all the documentation for setting up a new CloudBot client instance, as well as the required configurations and needed APIs/external softwares.
8 | - Dev: The devs folder contains documention for developers who wish to get a reference of the CloudBot API and code paradigms.
9 | - Etc: This folder holds all the stuff that didn't relate to any of the other two at all.
10 |
11 | For each set of docs, a PDF and HTML version is also available.
12 |
13 | ## Refresh
14 |
15 | Refresh (this repo) is the newest CloudBot, which cleans up the internals of the bot and brings support for Python 3.4.
16 |
17 | ## Developers
18 | * [Luke Rogers](http://git.io/theluke)
19 | * [neersighted](http://git.io/neersighted)
20 | * [daboross](http://git.io/dabo)
21 | * [foxlet](http://git.io/foxlet)
22 |
23 | ### Thanks to
24 | * [rmmh](http://git.io/rmmh)
25 | * [The Noodle](https://github.com/thenoodle68)
26 |
--------------------------------------------------------------------------------
/docs/dev/main.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/dev/main.md
--------------------------------------------------------------------------------
/docs/user/configuration.md:
--------------------------------------------------------------------------------
1 | ###Configuration
2 |
3 | This left blank for now.
4 |
--------------------------------------------------------------------------------
/docs/user/googlecustomsearch_id.md:
--------------------------------------------------------------------------------
1 | # Setting up the Google Custom Search Engine
2 |
3 | ## Introduction
4 | In this guide, we will cover the setup of the Custom Search Engine for use within CloudBot. This feature is used by:
5 | - plugins/google.py
6 |
--------------------------------------------------------------------------------
/docs/user/googledevconsole_api.md:
--------------------------------------------------------------------------------
1 | # Setting up the Google Developers Console API
2 |
3 | ## Introduction
4 | In this guide, we will cover the setup of the GDC API for use within CloudBot. This API is used by:
5 | - plugins/books.py
6 | - plugins/youtube.py
7 | - plugins/google_translate.py
8 |
9 | ## 1 - Sign Up for the Google Developers Console
10 | You can create a GDC account at https://console.developers.google.com/. You need to create a Google account or use an existing one.
11 |
12 | ## 2 - Create a new project
13 | Select ***Create New Project*** if you haven't already done so:
14 |
15 | 
16 |
17 | Give your bot a name (and optionally change the name of the project ID), agree to the Terms of Service, then select ***Create***.
18 |
19 | 
20 |
21 | ## 3 - Enable APIs
22 |
23 | Once you have created the project, select it in the main panel (if it already hasn't been), then on the sidebar go to **APIs and Auth -> APIs**. Scroll through the list to select ***ON*** for the following services:
24 |
25 | - Books API
26 | - Youtube Data API v3
27 | - Geocoding API
28 | - Time Zone API
29 | - Optional: Google Custom Search API, if using Google Search
30 | - Optional: Translate API, if using Google Translate
31 |
32 | Optional APIs are only used if you got the right modules or a payment type to use.
33 |
34 | 
35 |
36 | For each API, you may have to first accept their individual Terms of Service, then select ***Accept***.
37 |
38 | 
39 |
40 | ## 4 - Generate an API Key
41 | GDC API services only need one key for all Google Services used. You must generate a key for each bot instance you plan to use. Go to **APIs and Auth -> Credentials** then select ***Create a new Key***
42 |
43 | 
44 |
45 | Select to create a ***Server Key***
46 |
47 | 
48 |
49 | Enter the Public IPs of the Cloudbot instance you plan to assign to this key. If you don't know it, running `wget -qO- http://icanhazip.com/` within your terminal should return it. Click ***Create***.
50 |
51 | 
52 |
53 | Your new key should now appear on the main panel, simply copy it to the *google_dev_key* object in your CloudBot's configuration.
54 |
55 | 
56 |
--------------------------------------------------------------------------------
/docs/user/img/cse_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/cse_1.png
--------------------------------------------------------------------------------
/docs/user/img/cse_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/cse_2.png
--------------------------------------------------------------------------------
/docs/user/img/cse_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/cse_3.png
--------------------------------------------------------------------------------
/docs/user/img/gdev_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/gdev_1.png
--------------------------------------------------------------------------------
/docs/user/img/gdev_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/gdev_2.png
--------------------------------------------------------------------------------
/docs/user/img/gdev_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/gdev_3.png
--------------------------------------------------------------------------------
/docs/user/img/gdev_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/gdev_4.png
--------------------------------------------------------------------------------
/docs/user/img/gdev_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/gdev_5.png
--------------------------------------------------------------------------------
/docs/user/img/gdev_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/gdev_6.png
--------------------------------------------------------------------------------
/docs/user/img/gdev_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/gdev_7.png
--------------------------------------------------------------------------------
/docs/user/img/gdev_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/gdev_8.png
--------------------------------------------------------------------------------
/docs/user/img/oc_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/oc_1.png
--------------------------------------------------------------------------------
/docs/user/img/oc_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/oc_2.png
--------------------------------------------------------------------------------
/docs/user/img/oc_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/oc_3.png
--------------------------------------------------------------------------------
/docs/user/img/wn_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/docs/user/img/wn_1.png
--------------------------------------------------------------------------------
/docs/user/octopart_api.md:
--------------------------------------------------------------------------------
1 | # Setting up the Octopart API
2 |
3 | ## Introduction
4 | In this guide, we will cover the setup of the Octopart API for use within CloudBot. This API is used by:
5 | - plugins/octopart.py
6 |
7 | ## 1 - Sign Up on the Octopart Website
8 | You must first create an Octopart API account over at https://octopart.com/api/home/.
9 |
10 | 
11 |
12 | ## 2 - Create a new project in the Dashboard
13 | Once you got an account, you can proceed to make a new API application project [in the Dashboard](https://octopart.com/api/dashboard).
14 |
15 | 
16 |
17 | ## 2 - Fill in the needed API information
18 | Next, enter a new application name for your CloudBot instance, as well as a webpage for your bot (or your organization), agree to the Terms of Use, then select "Register application".
19 |
20 | 
21 |
22 | ## 3 - Get your API key
23 | Once in the dashboard, you can get the API key from the table shown.
24 |
--------------------------------------------------------------------------------
/docs/user/optout.md:
--------------------------------------------------------------------------------
1 | # Hook OptOuts
2 |
3 | - The `core.optout` plugin allows channels to enable/disable specific hooks matching a pattern.
4 | - Both the channel and hook parameters accept glob patterns.
5 | - OptOut checks are done in order from the most specific patterns to the most broad.
6 |
7 | ### Disabling a hook
8 | `optout plugin.command_func disable`
9 |
10 | ### Enabling a globally disabled hook
11 | `optout plugin.command_func enable`
12 |
13 | ### Globally disabling a hook
14 | `optout #* plugin.command_func disable`
15 |
16 | ## Examples
17 | #### Disabling all attack commands in a channel
18 | `optout attacks.* disable`
19 |
20 | #### Allowing `attacks.compliment` while still not allowing other attacks
21 | `optout attacks.* disable`
22 |
23 | `optout attacks.compliment enable`
24 |
25 | #### Globally disable the quote command
26 | `optout #* quote.quote disable`
27 |
--------------------------------------------------------------------------------
/docs/user/wordnik_api.md:
--------------------------------------------------------------------------------
1 | # Setting up the Wordnik API
2 |
3 | ## Introduction
4 | In this guide, we will cover the setup of the Wordnik API for use within CloudBot. This API is used by:
5 | - plugins/wordnik.py
6 |
7 | ## 1 - Sign Up for a Wordnik Developer Account
8 | You can create an account at http://developer.wordnik.com/. Once you have one, simply fill out the form at that very page.
9 |
10 | 
11 |
--------------------------------------------------------------------------------
/format_json.py:
--------------------------------------------------------------------------------
1 | """
2 | Format all JSON files in the bot core in a consistent manor
3 | """
4 |
5 | import collections
6 | import json
7 | from pathlib import Path
8 |
9 | path = Path().resolve()
10 |
11 | for file in path.rglob("*.json"):
12 | print(file)
13 | with file.open(encoding='utf8') as f:
14 | data = json.load(f, object_pairs_hook=collections.OrderedDict)
15 |
16 | with file.open('w', encoding='utf8') as f:
17 | print(json.dumps(data, indent=4), file=f)
18 |
--------------------------------------------------------------------------------
/plugins/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/plugins/__init__.py
--------------------------------------------------------------------------------
/plugins/animal_gifs.py:
--------------------------------------------------------------------------------
1 | """
2 | All GIFs courtesy of http://bestanimations.com/
3 | """
4 | import random
5 | from urllib.parse import urljoin
6 |
7 | from cloudbot import hook
8 | from cloudbot.util.http import get_soup
9 |
10 | BASE_URL = "http://bestanimations.com/Animals/Mammals/Dogs/"
11 | DOG_PAGES = (
12 | "Dogs.html",
13 | "Dogs2.html", # Pugs
14 | "Dogs3.html", # Puppies
15 | )
16 |
17 |
18 | def get_gifs(url):
19 | soup = get_soup(url)
20 | container = soup.find('div', class_="row")
21 | gifs = [urljoin(url, elem["src"]) for elem in container.find_all('img')]
22 | return gifs
23 |
24 |
25 | def get_random_gif(url):
26 | return random.choice(get_gifs(url))
27 |
28 |
29 | @hook.command(autohelp=False)
30 | def doggifs(reply):
31 | """- Returns a random dog GIF from http://bestanimations.com/"""
32 | page = random.choice(DOG_PAGES)
33 | url = urljoin(BASE_URL, page)
34 | try:
35 | return get_random_gif(url)
36 | except Exception:
37 | reply("Error occurred when retrieving GIF")
38 | raise
39 |
--------------------------------------------------------------------------------
/plugins/autojoin.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from collections import defaultdict
3 | from threading import RLock
4 |
5 | from sqlalchemy import PrimaryKeyConstraint, Column, String, Table, and_
6 | from sqlalchemy.exc import IntegrityError
7 |
8 | from cloudbot import hook
9 | from cloudbot.util import database
10 |
11 | table = Table(
12 | 'autojoin',
13 | database.metadata,
14 | Column('conn', String),
15 | Column('chan', String),
16 | PrimaryKeyConstraint('conn', 'chan')
17 | )
18 |
19 | chan_cache = defaultdict(set)
20 | db_lock = RLock()
21 |
22 |
23 | def get_channels(db, conn):
24 | return db.execute(table.select().where(table.c.conn == conn.name.casefold())).fetchall()
25 |
26 |
27 | @hook.on_start
28 | def load_cache(db):
29 | with db_lock:
30 | chan_cache.clear()
31 | for row in db.execute(table.select()):
32 | chan_cache[row['conn']].add(row['chan'])
33 |
34 |
35 | @hook.irc_raw('376')
36 | @asyncio.coroutine
37 | def do_joins(conn):
38 | join_throttle = conn.config.get("join_throttle", 0.4)
39 | for chan in chan_cache[conn.name]:
40 | conn.join(chan)
41 | yield from asyncio.sleep(join_throttle)
42 |
43 |
44 | @hook.irc_raw('JOIN', singlethread=True)
45 | def add_chan(db, conn, chan, nick):
46 | chans = chan_cache[conn.name]
47 | chan = chan.casefold()
48 | if nick.casefold() == conn.nick.casefold() and chan not in chans:
49 | with db_lock:
50 | try:
51 | db.execute(table.insert().values(conn=conn.name.casefold(), chan=chan.casefold()))
52 | except IntegrityError:
53 | db.rollback()
54 | else:
55 | db.commit()
56 |
57 | load_cache(db)
58 |
59 |
60 | @hook.irc_raw('PART', singlethread=True)
61 | def on_part(db, conn, chan, nick):
62 | if nick.casefold() == conn.nick.casefold():
63 | with db_lock:
64 | db.execute(
65 | table.delete().where(and_(table.c.conn == conn.name.casefold(), table.c.chan == chan.casefold())))
66 | db.commit()
67 |
68 | load_cache(db)
69 |
70 |
71 | @hook.irc_raw('KICK', singlethread=True)
72 | def on_kick(db, conn, chan, target):
73 | on_part(db, conn, chan, target)
74 |
--------------------------------------------------------------------------------
/plugins/bible.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | from cloudbot import hook
4 |
5 |
6 | @hook.command("bible", "passage", singlethread=True)
7 | def bible(text, reply):
8 | """ - Prints the specified passage from the Bible"""
9 | passage = text.strip()
10 | params = {
11 | 'passage': passage,
12 | 'formatting': 'plain',
13 | 'type': 'json'
14 | }
15 | try:
16 | r = requests.get("https://labs.bible.org/api", params=params)
17 | r.raise_for_status()
18 | response = r.json()[0]
19 | except Exception:
20 | reply("Something went wrong, either you entered an invalid passage or the API is down.")
21 | raise
22 |
23 | book = response['bookname']
24 | ch = response['chapter']
25 | ver = response['verse']
26 | txt = response['text']
27 | return "\x02{} {}:{}\x02 {}".format(book, ch, ver, txt)
28 |
--------------------------------------------------------------------------------
/plugins/books.py:
--------------------------------------------------------------------------------
1 | import requests
2 | from requests import HTTPError
3 |
4 | from cloudbot import hook
5 | from cloudbot.util import formatting, web
6 |
7 | base_url = 'https://www.googleapis.com/books/v1/'
8 | book_search_api = base_url + 'volumes?'
9 |
10 |
11 | @hook.on_start()
12 | def load_key(bot):
13 | global dev_key
14 | dev_key = bot.config.get("api_keys", {}).get("google_dev_key", None)
15 |
16 |
17 | @hook.command("books", "gbooks")
18 | def books(text, reply):
19 | """ - Searches Google Books for ."""
20 | if not dev_key:
21 | return "This command requires a Google Developers Console API key."
22 |
23 | request = requests.get(book_search_api, params={"q": text, "key": dev_key, "country": "US"})
24 |
25 | try:
26 | request.raise_for_status()
27 | except HTTPError:
28 | reply("Bing API error occurred.")
29 | raise
30 |
31 | json = request.json()
32 |
33 | if json.get('error'):
34 | if json['error']['code'] == 403:
35 | print(json['error']['message'])
36 | return "The Books API is off in the Google Developers Console (or check the console)."
37 | else:
38 | return 'Error performing search.'
39 |
40 | if json['totalItems'] == 0:
41 | return 'No results found.'
42 |
43 | book = json['items'][0]['volumeInfo']
44 | title = book['title']
45 | try:
46 | author = book['authors'][0]
47 | except KeyError:
48 | try:
49 | author = book['publisher']
50 | except KeyError:
51 | author = "Unknown Author"
52 |
53 | try:
54 | description = formatting.truncate_str(book['description'], 130)
55 | except KeyError:
56 | description = "No description available."
57 |
58 | try:
59 | year = book['publishedDate'][:4]
60 | except KeyError:
61 | year = "No Year"
62 |
63 | try:
64 | page_count = book['pageCount']
65 | pages = ' - \x02{:,}\x02 page{}'.format(page_count, "s"[page_count == 1:])
66 | except KeyError:
67 | pages = ''
68 |
69 | link = web.shorten(book['infoLink'], service="goo.gl", key=dev_key)
70 |
71 | return "\x02{}\x02 by \x02{}\x02 ({}){} - {} - {}".format(title, author, year, pages, description, link)
72 |
--------------------------------------------------------------------------------
/plugins/brew.py:
--------------------------------------------------------------------------------
1 | import requests
2 | from requests import HTTPError
3 |
4 | from cloudbot import hook
5 |
6 | api_url = "http://api.brewerydb.com/v2/search?format=json"
7 |
8 |
9 | @hook.on_start()
10 | def load_key(bot):
11 | global api_key
12 | api_key = bot.config.get("api_keys", {}).get("brewerydb", None)
13 |
14 |
15 | @hook.command('brew')
16 | def brew(text, reply):
17 | """ - returns the first brewerydb search result for """
18 |
19 | if not api_key:
20 | return "No brewerydb API key set."
21 |
22 | params = {'key': api_key, 'type': 'beer', 'withBreweries': 'Y', 'q': text}
23 | request = requests.get(api_url, params=params)
24 |
25 | try:
26 | request.raise_for_status()
27 | except HTTPError:
28 | reply("Failed to fetch info ({})".format(request.status_code))
29 | raise
30 |
31 | response = request.json()
32 |
33 | output = "No results found."
34 |
35 | try:
36 | if 'totalResults' in response:
37 | beer = response['data'][0]
38 | brewery = beer['breweries'][0]
39 |
40 | style = 'unknown style'
41 | if 'style' in beer:
42 | style = beer['style']['shortName']
43 |
44 | abv = '?.?'
45 | if 'abv' in beer:
46 | abv = beer['abv']
47 |
48 | url = '[no website found]'
49 | if 'website' in brewery:
50 | url = brewery['website']
51 |
52 | content = {
53 | 'name': beer['nameDisplay'],
54 | 'style': style,
55 | 'abv': abv,
56 | 'brewer': brewery['name'],
57 | 'url': url
58 | }
59 |
60 | output = "{name} by {brewer} ({style}, {abv}% ABV) - {url}" \
61 | .format(**content)
62 |
63 | except Exception as e:
64 | print(e)
65 | reply("Error parsing results.")
66 | raise
67 |
68 | return output
69 |
--------------------------------------------------------------------------------
/plugins/cats.py:
--------------------------------------------------------------------------------
1 | import requests
2 | from requests import HTTPError
3 |
4 | from cloudbot import hook
5 |
6 |
7 | def get_data(url, reply, bot, params=None):
8 | try:
9 | r = requests.get(url, headers={'User-Agent': bot.user_agent}, params=params)
10 | r.raise_for_status()
11 | except HTTPError:
12 | reply("API error occurred.")
13 | raise
14 |
15 | return r
16 |
17 |
18 | @hook.command(autohelp=False)
19 | def cats(reply, bot):
20 | """- gets a fucking fact about cats."""
21 | r = get_data('https://catfact.ninja/fact', reply, bot, params={'max_length': 100})
22 | json = r.json()
23 | response = json['fact']
24 | return response
25 |
26 |
27 | @hook.command(autohelp=False)
28 | def catgifs(reply, bot):
29 | """- gets a fucking cat gif."""
30 | r = get_data("http://marume.herokuapp.com/random.gif", reply, bot)
31 | return "OMG A CAT GIF: {}".format(r.url)
32 |
--------------------------------------------------------------------------------
/plugins/chatbot.py:
--------------------------------------------------------------------------------
1 | from cleverwrap import CleverWrap
2 |
3 | from cloudbot import hook
4 |
5 |
6 | @hook.on_start()
7 | def get_key(bot):
8 | global api_key, cb
9 | api_key = bot.config.get("api_keys", {}).get("cleverbot", None)
10 | cb = CleverWrap(api_key)
11 |
12 |
13 | @hook.command("ask", "gonzo", "gonzobot", "cleverbot", "cb")
14 | def chitchat(text):
15 | """ - chat with cleverbot.com"""
16 | if not api_key:
17 | return "Please add an API key from http://www.cleverbot.com/api to enable this feature."
18 | return cb.say(text)
19 |
--------------------------------------------------------------------------------
/plugins/cheer.py:
--------------------------------------------------------------------------------
1 | import random
2 | import re
3 | from pathlib import Path
4 |
5 | from cloudbot import hook
6 |
7 | cheer_re = re.compile(r'\\o/', re.IGNORECASE)
8 |
9 | cheers = []
10 |
11 |
12 | @hook.on_start
13 | def load_cheers(bot):
14 | cheers.clear()
15 | data_file = Path(bot.data_dir) / "cheers.txt"
16 | with data_file.open(encoding='utf-8') as f:
17 | cheers.extend(line.strip() for line in f if not line.startswith('//'))
18 |
19 |
20 | @hook.regex(cheer_re)
21 | def cheer(chan, message):
22 | """
23 | :type chan: str
24 | """
25 | shit = random.choice(cheers)
26 | message(shit, chan)
27 |
--------------------------------------------------------------------------------
/plugins/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwardslabs/CloudBot/a0853d29e01e755fa9ce92e5f7394704840e9efb/plugins/core/__init__.py
--------------------------------------------------------------------------------
/plugins/core/chan_log.py:
--------------------------------------------------------------------------------
1 | import traceback
2 |
3 | from requests.exceptions import RequestException
4 |
5 | from cloudbot import hook
6 | from cloudbot.util import web
7 |
8 |
9 | def _dump_attrs(obj):
10 | for name in dir(obj):
11 | if not name.startswith('_'):
12 | yield name, getattr(obj, name, None)
13 |
14 |
15 | @hook.post_hook
16 | def on_hook_end(error, launched_hook, launched_event, admin_log):
17 | should_broadcast = True
18 | if error is not None:
19 | messages = [
20 | "Error occurred in {}.{}".format(launched_hook.plugin.title, launched_hook.function_name)
21 | ]
22 |
23 | try:
24 | lines = traceback.format_exception(*error)
25 | last_line = lines[-1]
26 | messages.append(last_line.strip())
27 | except Exception as e:
28 | messages.append("Error occurred while formatting error {}: {}".format(type(e), e))
29 | else:
30 | try:
31 | url = web.paste('\n'.join(lines))
32 | messages.append("Traceback: " + url)
33 | except Exception as e:
34 | messages.append("Error occurred while gathering traceback {}: {}".format(type(e), e))
35 |
36 | try:
37 | lines = ["{} = {}".format(k, v) for k, v in _dump_attrs(launched_event)]
38 | exc_type, exc, exc_tb = error
39 |
40 | lines.append("")
41 | lines.append("Error data:")
42 | lines.extend("{} = {}".format(k, v) for k, v in _dump_attrs(exc))
43 |
44 | if isinstance(exc, RequestException):
45 | if exc.request is not None:
46 | req = exc.request
47 | lines.append("")
48 | lines.append("Request Info:")
49 | lines.extend("{} = {}".format(k, v) for k, v in _dump_attrs(req))
50 |
51 | if exc.response is not None:
52 | response = exc.response
53 | lines.append("")
54 | lines.append("Response Info:")
55 | lines.extend("{} = {}".format(k, v) for k, v in _dump_attrs(response))
56 |
57 | url = web.paste('\n'.join(lines))
58 | messages.append("Event: " + url)
59 | except Exception as e:
60 | messages.append("Error occurred while gathering error data {}: {}".format(type(e), e))
61 |
62 | for message in messages:
63 | admin_log(message, should_broadcast)
64 |
--------------------------------------------------------------------------------
/plugins/core/core_connect.py:
--------------------------------------------------------------------------------
1 | from cloudbot import hook
2 |
3 |
4 | @hook.connect(priority=0, clients="irc")
5 | def conn_pass(conn):
6 | conn.set_pass(conn.config["connection"].get("password"))
7 |
8 |
9 | @hook.connect(priority=10)
10 | def conn_nick(conn):
11 | conn.set_nick(conn.nick)
12 |
13 |
14 | @hook.connect(priority=20, clients="irc")
15 | def conn_user(conn):
16 | conn.cmd(
17 | "USER", conn.config.get('user', 'cloudbot'), "3", "*",
18 | conn.config.get('realname', 'CloudBot - https://git.io/CloudBot')
19 | )
20 |
--------------------------------------------------------------------------------
/plugins/core/core_ctcp.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import time
3 |
4 | import cloudbot
5 | from cloudbot import hook
6 | from cloudbot.event import EventType
7 |
8 |
9 | # CTCP responses
10 | @asyncio.coroutine
11 | @hook.event([EventType.other])
12 | def ctcp_version(notice, irc_ctcp_text):
13 | if irc_ctcp_text:
14 | if irc_ctcp_text.startswith("VERSION"):
15 | notice("\x01VERSION gonzobot a fork of Cloudbot {} - https://snoonet.org/gonzobot\x01".format(
16 | cloudbot.__version__))
17 | elif irc_ctcp_text.startswith("PING"):
18 | notice('\x01{}\x01'.format(
19 | irc_ctcp_text)) # Bot should return exactly what the user sends as the ping parameter
20 | elif irc_ctcp_text.startswith("TIME"):
21 | notice('\x01TIME {}\x01'.format(time.asctime())) # General convention is to return the asc time
22 |
--------------------------------------------------------------------------------
/plugins/core/core_hooks.py:
--------------------------------------------------------------------------------
1 | from cloudbot import hook
2 | from cloudbot.hook import Priority
3 |
4 |
5 | @hook.sieve(priority=Priority.LOWEST)
6 | def cmd_autohelp(bot, event, _hook):
7 | if _hook.type == "command" and _hook.auto_help and not event.text and _hook.doc is not None:
8 | event.notice_doc()
9 | return None
10 |
11 | return event
12 |
13 |
14 | @hook.post_hook(priority=Priority.LOWEST)
15 | def do_reply(result, error, launched_event, launched_hook):
16 | if launched_hook.type in ("sieve", "on_start", "on_stop"):
17 | return
18 |
19 | if error is None and result is not None:
20 | if isinstance(result, (list, tuple)):
21 | # if there are multiple items in the response, return them on multiple lines
22 | launched_event.reply(*result)
23 | else:
24 | launched_event.reply(result)
25 |
--------------------------------------------------------------------------------
/plugins/core/core_out.py:
--------------------------------------------------------------------------------
1 | """
2 | Core filters for IRC raw lines
3 | """
4 |
5 | from cloudbot import hook
6 | from cloudbot.hook import Priority
7 | from cloudbot.util import colors
8 |
9 | NEW_LINE_TRANS_TBL = str.maketrans({
10 | '\r': None,
11 | '\n': None,
12 | '\0': None,
13 | })
14 |
15 |
16 | @hook.irc_out(priority=Priority.HIGHEST)
17 | def strip_newlines(line, conn):
18 | """
19 | Removes newline characters from a message
20 | :param line: str
21 | :param conn: cloudbot.clients.irc.IrcClient
22 | :return: str
23 | """
24 | do_strip = conn.config.get("strip_newlines", True)
25 | if do_strip:
26 | return line.translate(NEW_LINE_TRANS_TBL)
27 | else:
28 | return line
29 |
30 |
31 | @hook.irc_out(priority=Priority.HIGH)
32 | def truncate_line(line, conn):
33 | line_len = conn.config.get("max_line_length", 510)
34 | return line[:line_len] + "\r\n"
35 |
36 |
37 | @hook.irc_out(priority=Priority.LOWEST)
38 | def encode_line(line, conn):
39 | if not isinstance(line, str):
40 | return line
41 |
42 | encoding = conn.config.get("encoding", "utf-8")
43 | errors = conn.config.get("encoding_errors", "replace")
44 | return line.encode(encoding, errors)
45 |
46 |
47 | @hook.irc_out(priority=Priority.HIGH)
48 | def strip_command_chars(parsed_line, conn, line):
49 | chars = conn.config.get("strip_cmd_chars", "!.@;$")
50 | if chars and parsed_line and parsed_line.command == "PRIVMSG" and parsed_line.parameters[-1][0] in chars:
51 | new_msg = colors.parse("$(red)[!!]$(clear) ") + parsed_line.parameters[-1]
52 | parsed_line.parameters[-1] = new_msg
53 | parsed_line.has_trail = True
54 | return parsed_line
55 |
56 | return line
57 |
--------------------------------------------------------------------------------
/plugins/core/plugin_control.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from operator import itemgetter
3 | from pathlib import Path
4 |
5 | from cloudbot import hook
6 | from cloudbot.util import web
7 | from cloudbot.util.formatting import gen_markdown_table
8 |
9 |
10 | @hook.command(permissions=["botcontrol"], autohelp=False)
11 | def pluginlist(bot):
12 | """- List all currently loaded plugins"""
13 | manager = bot.plugin_manager
14 | plugins = [
15 | (plugin.title, str(Path(plugin.file_path).resolve().relative_to(bot.base_dir)))
16 | for plugin in manager.plugins.values()
17 | ]
18 | plugins.sort(key=itemgetter(0))
19 | table = gen_markdown_table(["Plugin", "Path"], plugins)
20 | return web.paste(table, service="hastebin")
21 |
22 |
23 | @hook.command(permissions=["botcontrol"])
24 | @asyncio.coroutine
25 | def pluginload(bot, text, reply):
26 | """ - (Re)load manually"""
27 | manager = bot.plugin_manager
28 | path = str(Path(text.strip()).resolve())
29 | was_loaded = path in manager.plugins
30 | coro = bot.plugin_manager.load_plugin(path)
31 |
32 | try:
33 | yield from coro
34 | except Exception:
35 | reply("Plugin failed to load.")
36 | raise
37 | else:
38 | return "Plugin {}loaded successfully.".format("re" if was_loaded else "")
39 |
40 |
41 | @hook.command(permissions=["botcontrol"])
42 | @asyncio.coroutine
43 | def pluginunload(bot, text):
44 | """ - Unload manually"""
45 | manager = bot.plugin_manager
46 | path = str(Path(text.strip()).resolve())
47 | is_loaded = path in manager.plugins
48 |
49 | if not is_loaded:
50 | return "Plugin not loaded, unable to unload."
51 |
52 | if (yield from manager.unload_plugin(path)):
53 | return "Plugin unloaded successfully."
54 |
55 | return "Plugin failed to unload."
56 |
--------------------------------------------------------------------------------
/plugins/correction.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | from cloudbot import hook
4 | from cloudbot.util.formatting import ireplace
5 |
6 | correction_re = re.compile(r"^[sS]/(?:(.*?)(? {}"
40 |
41 | mod_msg = ireplace(re.escape(mod_msg), find_esc, "\x02" + replace_esc + "\x02")
42 |
43 | mod_msg = unescape_re.sub(r"\1", mod_msg)
44 |
45 | message("Correction, {}".format(fmt.format(name, mod_msg)))
46 |
47 | if nick.lower() == name.lower():
48 | msg = ireplace(re.escape(msg), find_esc, replace_esc)
49 | msg = unescape_re.sub(r"\1", msg)
50 | conn.history[chan].append((name, timestamp, msg))
51 |
52 | break
53 |
--------------------------------------------------------------------------------
/plugins/cypher.py:
--------------------------------------------------------------------------------
1 | """
2 | cypher.py
3 |
4 | Ciphers and deciphers strings.
5 |
6 | Created By:
7 | - Tom
8 |
9 | Modified By:
10 | - Fletcher Boyd
11 | - Dabo Ross
12 | - Luke Rogers
13 |
14 | License:
15 | GPL v3
16 | """
17 |
18 | import base64
19 | import binascii
20 |
21 | from cloudbot import hook
22 |
23 |
24 | def encode(password, text):
25 | """
26 | :type password: str
27 | :type text: str
28 | """
29 | enc = []
30 | for i in range(len(text)):
31 | key_c = password[i % len(password)]
32 | enc_c = chr((ord(text[i]) + ord(key_c)) % 256)
33 | enc.append(enc_c)
34 | return base64.urlsafe_b64encode("".join(enc).encode()).decode()
35 |
36 |
37 | def decode(password, encoded, notice):
38 | """
39 | :type password: str
40 | :type encoded: str
41 | """
42 | dec = []
43 | try:
44 | encoded_bytes = base64.urlsafe_b64decode(encoded.encode()).decode()
45 | except binascii.Error:
46 | notice("Invalid input '{}'".format(encoded))
47 | return
48 | for i in range(len(encoded_bytes)):
49 | key_c = password[i % len(password)]
50 | dec_c = chr((256 + ord(encoded_bytes[i]) - ord(key_c)) % 256)
51 | dec.append(dec_c)
52 | return "".join(dec)
53 |
54 |
55 | @hook.command("cypher", "cipher")
56 | def cypher(text, message, notice):
57 | """ -- cyphers with """
58 | split = text.split(None, 1)
59 | if len(split) < 2:
60 | notice(cypher.__doc__)
61 | return
62 | password = split[0]
63 | plaintext = split[1]
64 | message(" " + encode(password, plaintext))
65 |
66 |
67 | @hook.command("decypher", "decipher")
68 | def decypher(text, message, notice):
69 | """ - decyphers with """
70 | split = text.split(None, 1)
71 | if len(split) < 2:
72 | notice(decypher.__doc__)
73 | return
74 | password = split[0]
75 | encoded = split[1]
76 | message(" " + decode(password, encoded, notice))
77 |
--------------------------------------------------------------------------------
/plugins/deals.py:
--------------------------------------------------------------------------------
1 | import feedparser
2 |
3 | from cloudbot import hook
4 | from cloudbot.util import web
5 |
6 |
7 | @hook.command('meh', autohelp=False)
8 | def meh():
9 | """- List the current meh.com deal."""
10 | url = "https://meh.com/deals.rss"
11 |
12 | feed = feedparser.parse(url)
13 | title = feed.entries[0].title
14 | link = web.try_shorten(feed.entries[0].link)
15 |
16 | return "meh.com: {} ({})".format(title, link)
17 |
18 |
19 | @hook.command('slickdeals', autohelp=False)
20 | def slickdeals():
21 | """- List the top 3 frontpage slickdeals.net deals."""
22 | url = "https://slickdeals.net/newsearch.php?mode=frontpage&searcharea=deals&searchin=first&rss=1"
23 |
24 | feed = feedparser.parse(url)
25 | items = (
26 | "{} ({})".format(item.title, web.try_shorten(item.link))
27 | for item in feed.entries[:3]
28 | )
29 |
30 | out = "slickdeals.net: " + ' \u2022 '.join(items)
31 |
32 | return out
33 |
--------------------------------------------------------------------------------
/plugins/dig.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | from cloudbot import hook
4 |
5 |
6 | @hook.command
7 | def dig(text, nick, notice):
8 | """ - returns a list of records for the specified domain valid record types are A, NS, TXT, and MX. If a record type is not chosen A will be the default."""
9 | args = text.split()
10 | domain = args.pop(0)
11 |
12 | if args:
13 | rtype = args.pop(0).upper()
14 | else:
15 | rtype = "A"
16 |
17 | if rtype not in ("A", "NS", "MX", "TXT"):
18 | rtype = "A"
19 |
20 | url = "http://dig.jsondns.org/IN/{}/{}".format(domain, rtype)
21 | r = requests.get(url)
22 | r.raise_for_status()
23 | results = r.json()
24 | if results['header']['rcode'] == "NXDOMAIN":
25 | return "no dns record for {} was found".format(domain)
26 | notice("The following records were found for \x02{}\x02: ".format(domain), nick)
27 | for r in range(len(results['answer'])):
28 | domain = results['answer'][r]['name']
29 | rtype = results['answer'][r]['type']
30 | ttl = results['answer'][r]['ttl']
31 | if rtype == "MX":
32 | rdata = results['answer'][r]['rdata'][1]
33 | elif rtype == "TXT":
34 | rdata = results['answer'][r]['rdata'][0]
35 | else:
36 | rdata = results['answer'][r]['rdata']
37 | notice("name: \x02{}\x02 type: \x02{}\x02 ttl: \x02{}\x02 rdata: \x02{}\x02".format(
38 | domain, rtype, ttl, rdata), nick)
39 |
--------------------------------------------------------------------------------
/plugins/dogpile.py:
--------------------------------------------------------------------------------
1 | import random
2 | import re
3 | from urllib import parse
4 |
5 | import requests
6 | from bs4 import BeautifulSoup
7 |
8 | from cloudbot import hook
9 |
10 | search_url = "http://dogpile.com/search"
11 |
12 | HEADERS = {
13 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19'
14 | }
15 |
16 |
17 | @hook.command("dpis", "gis")
18 | def dogpileimage(text):
19 | """ - Uses the dogpile search engine to search for images."""
20 | image_url = search_url + "/images"
21 | params = {'q': " ".join(text.split())}
22 | r = requests.get(image_url, params=params, headers=HEADERS)
23 | r.raise_for_status()
24 | soup = BeautifulSoup(r.content)
25 | data = soup.find_all("script")[6].string
26 | link_re = re.compile('"url":"(.*?)",')
27 | linklist = link_re.findall(data)
28 | if not linklist:
29 | return "No results returned."
30 |
31 | image = parse.unquote(parse.unquote(random.choice(linklist)).split('ru=')[1].split('&')[0])
32 | return image
33 |
34 |
35 | @hook.command("dp", "g", "dogpile")
36 | def dogpile(text):
37 | """ - Uses the dogpile search engine to find shit on the web."""
38 | web_url = search_url + "/web"
39 | params = {'q': " ".join(text.split())}
40 | r = requests.get(web_url, params=params, headers=HEADERS)
41 | r.raise_for_status()
42 | soup = BeautifulSoup(r.content)
43 | results = soup.find('div', id="webResults")
44 | if not results:
45 | return "No results found."
46 |
47 | result_url = parse.unquote(
48 | parse.unquote(results.find_all('a', {'class': 'resultDisplayUrl'})[0]['href']).split(
49 | 'ru=')[1].split('&')[0])
50 | result_description = results.find_all('div', {'class': 'resultDescription'})[0].text
51 | return "{} -- \x02{}\x02".format(result_url, result_description)
52 |
--------------------------------------------------------------------------------
/plugins/domainr.py:
--------------------------------------------------------------------------------
1 | from cloudbot import hook
2 | from cloudbot.util import http
3 |
4 | formats = {
5 | "taken": "\x034{domain}\x0f{path}",
6 | "available": "\x033{domain}\x0f{path}",
7 | "other": "\x031{domain}\x0f{path}"
8 | }
9 |
10 |
11 | def format_domain(domain):
12 | """
13 | :type domain: dict[str, str]
14 | """
15 | if domain["availability"] in formats:
16 | domainformat = formats[domain["availability"]]
17 | else:
18 | domainformat = formats["other"]
19 | return domainformat.format(**domain)
20 |
21 |
22 | @hook.command("domain", "domainr")
23 | def domainr(text):
24 | """ - uses domain.nr's API to search for a domain, and similar domains
25 | :type text: str
26 | """
27 | try:
28 | data = http.get_json('http://domai.nr/api/json/search?q=' + text)
29 | except (http.URLError, http.HTTPError):
30 | return "Unable to get data for some reason. Try again later."
31 | if data['query'] == "":
32 | return "An error occurred: {status} - {message}".format(**data['error'])
33 |
34 | domains = [format_domain(domain) for domain in data["results"]]
35 | return "Domains: {}".format(", ".join(domains))
36 |
--------------------------------------------------------------------------------
/plugins/dragonvale.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | import requests
4 | from bs4 import BeautifulSoup
5 | from requests import HTTPError
6 |
7 | from cloudbot import hook
8 | from cloudbot.util.timeparse import time_parse
9 |
10 | search_url = "http://dragonvale.wikia.com/api/v1/Search/list"
11 |
12 | egg_calc_url = "http://www.dragonvalebreedingguide.com/dragonvale-calculator"
13 |
14 |
15 | def striphtml(data):
16 | string = re.compile(r'<.*?>')
17 | return string.sub('', data)
18 |
19 |
20 | @hook.command("dragon", "ds")
21 | def dragonsearch(text, reply):
22 | """ - Searches the dragonvale wiki for the specified text."""
23 | params = {
24 | "query": text.strip(),
25 | "limit": 1
26 | }
27 |
28 | r = requests.get(search_url, params=params)
29 |
30 | try:
31 | r.raise_for_status()
32 | except HTTPError:
33 | reply("The API returned error code {}.".format(r.status_code))
34 | raise
35 |
36 | if not r.status_code == 200:
37 | return "The API returned error code {}.".format(r.status_code)
38 |
39 | data = r.json()["items"][0]
40 | out = "\x02{}\x02 -- {}: {}".format(data["title"], striphtml(data["snippet"]).split("…")[0].strip(),
41 | data["url"])
42 | return out
43 |
44 |
45 | @hook.command("eggcalc", "dragoncalc", "dc")
46 | def egg_calculator(text):
47 | """