├── .gitignore
├── .travis.yml
├── LICENSE
├── Makefile
├── Procfile
├── README.md
├── bin
└── charmander
├── charmander
├── __init__.py
├── charmander.py
├── dummyserver.py
├── extensions
│ ├── __init__.py
│ ├── calc.py
│ ├── commit.py
│ ├── emoji.py
│ ├── emojidic.py
│ ├── google.py
│ ├── help.py
│ ├── log.py
│ ├── urban.py
│ ├── wiki.py
│ └── youtube.py
└── server.py
├── output_ZrAcxK.gif
├── requirements.txt
├── setup.cfg
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | #####=== Python ===#####
2 |
3 | # Byte-compiled / optimized / DLL files
4 | __pycache__/
5 | *.py[cod]
6 |
7 | # C extensions
8 | *.so
9 |
10 | # Distribution / packaging
11 | .Python
12 | env/
13 | build/
14 | develop-eggs/
15 | dist/
16 | downloads/
17 | eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .cache
42 | nosetests.xml
43 | coverage.xml
44 |
45 | # Translations
46 | *.mo
47 | *.pot
48 |
49 | # Django stuff:
50 | *.log
51 |
52 | # Sphinx documentation
53 | docs/_build/
54 |
55 | # PyBuilder
56 | target/
57 |
58 |
59 | #####====Pycharm========
60 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
61 |
62 | *.iml
63 |
64 | ## Directory-based project format:
65 | .idea/
66 | # if you remove the above rule, at least ignore the following:
67 |
68 | # User-specific stuff:
69 | # .idea/workspace.xml
70 | # .idea/tasks.xml
71 | # .idea/dictionaries
72 |
73 | # Sensitive or high-churn files:
74 | # .idea/dataSources.ids
75 | # .idea/dataSources.xml
76 | # .idea/sqlDataSources.xml
77 | # .idea/dynamic.xml
78 | # .idea/uiDesigner.xml
79 |
80 | # Gradle:
81 | # .idea/gradle.xml
82 | # .idea/libraries
83 |
84 | # Mongo Explorer plugin:
85 | # .idea/mongoSettings.xml
86 |
87 | ## File-based project format:
88 | *.ipr
89 | *.iws
90 |
91 | ## Plugin-specific files:
92 |
93 | # IntelliJ
94 | out/
95 |
96 | # mpeltonen/sbt-idea plugin
97 | .idea_modules/
98 |
99 | # JIRA plugin
100 | # JIRA plugin
101 | atlassian-ide-plugin.xml
102 |
103 | # Crashlytics plugin (for Android Studio and IntelliJ)
104 | com_crashlytics_export_strings.xml
105 | crashlytics.properties
106 | crashlytics-build.properties
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - '2.6'
4 | - '2.7'
5 | - '3.4'
6 | - "pypy"
7 | install: pip install -r requirements.txt && python setup.py install
8 | script: make test
9 | notifications:
10 | slack:
11 | secure: qTTpPJZRjkFNlWUQd4M4odA+/QnuEqoD3ole8tQA+oFo2hzA7toQbAorgKd20mJ0VMLbrTxUDeCHwuLH6EYs0k9gnaDx7J8gaohW3ePHU6vncGfSh/9r8jCLHOdD/23ySm6D4TKR5SV7izNS+4WyqLqeLugdk5rFCwW4JJS118U=
12 | email: false
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Vikesh Tiwari
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: testall
2 | testall: requirements
3 | tox
4 |
5 | .PHONY: test
6 | test: install
7 | LANG=en_us.UTF-8 NOSE_COVER_PACKAGE=charmander nosetests -s --nologcapture --with-coverage
8 |
9 | .PHONY: clean
10 | clean:
11 | rm -rf build dist charmander.egg-info
12 |
13 | .PHONY: run
14 | run: install
15 | bin/charmander
16 |
17 | .PHONY: repl
18 | repl: install
19 | bin/charmander -t
20 |
21 | # non-empty if we're on python 2.6
22 | PYTHON2_6 = $(shell python --version 2>&1 | grep 2.6)
23 |
24 | .PHONY: requirements
25 | requirements:
26 | pip install -r requirements.txt
27 | ifneq ($(PYTHON2_6), )
28 | pip install -r requirements-2.6.txt
29 | endif
30 |
31 | .PHONY: install
32 | install: requirements
33 | python setup.py install
34 | make clean
35 |
36 | .PHONY: publish
37 | publish:
38 | pandoc -s -w rst README.md -o README.rs
39 | python setup.py sdist upload
40 | rm README.rs
41 |
42 | .PHONY: flake8
43 | flake8:
44 | flake8 charmander test
45 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | worker: python bin/charmander
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Charmander
2 |
3 |
4 |
5 |
6 | ---
7 | ### Donate ($1) - Help me in creating more awesome open source projects
8 |
9 | [
](https://www.paypal.me/vikeshtiwari/1)
10 |
11 | ---
12 |
13 | Everything right in your slack channel!
14 | ------------------------------------
15 |
16 | Charmander is a bot for Slack to do everything for you. You don't need to open browser to search for something because Charmander searches everything from Google and shows top results to you right in your slack channel!
17 |
18 | [](http://www.youtube.com/watch?v=u4D6xyIHjY4)
19 |
20 | Charmander Supports
21 | ------------------
22 | - Google [Search Anything from slack] - [Done](https://github.com/vicky002/Charmander/blob/master/charmander/extensions/google.py)!
23 | - Mathematical Calculations [Do mathematical calculations] - done!
24 | - Weather [Check current weather in your city]
25 | - Maps [See maps ]
26 | - urban [See meaning of words] - [Done](https://github.com/vicky002/Charmander/blob/master/charmander/extensions/urban.py)!
27 | - Commits [Get random commit messages] - [Done](https://github.com/vicky002/Charmander/blob/master/charmander/extensions/commit.py)!
28 | - Gifs [Post gifs]
29 | - Emoji [Post emojis] - [Done](https://github.com/vicky002/Charmander/blob/master/charmander/extensions/emoji.py)!
30 | - Stock [Check stocks ]
31 | - Images [Post images]
32 | - YouTube [search videos] - [Done](https://github.com/vicky002/Charmander/blob/master/charmander/extensions/youtube.py)!
33 | - Shuffler [Shuffle anything]
34 | - Wiki [Search from Wikipedia] - [Done](https://github.com/vicky002/Charmander/blob/master/charmander/extensions/wiki.py)!
35 |
36 | **Why am I doing this?**
37 | I created a bot for Slack [The L](https://github.com/vicky002/slack-TheL) which uses Wolframalpha API, check [Tutorial here](http://eulercoder.me/posts/How-to-create-Slack-Bot-using-wolframalpha-API/). Wolframalpha allows only 2000 calls per month. It was hard to set up and was not of that much use. Charmander directly searches from the Google to answer your queries. And it's all free to use. Its very easy to setup in your slack channel.
38 |
39 | # Installation
40 |
41 | [Will be updated soon]
42 |
43 | # Getting Started
44 |
45 | [Will be updated soon]
46 |
47 | # Contributing
48 |
49 | [Will be updated soon]
50 |
51 |
52 | # Issues
53 |
54 | Check [Issue tracker](https://github.com/vicky002/Charmander/issues) for all the issues.
55 |
56 | # Licence
57 |
58 | [MIT](https://github.com/vicky002/Charmander/blob/master/LICENSE) Licence (c) [Vikesh Tiwari](https://github.com/vicky002)
59 |
60 | Built with :heart: in python
61 |
--------------------------------------------------------------------------------
/bin/charmander:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from charmander import main
4 | import argparse
5 |
6 | parser = argparse.ArgumentParser(description="Run the charmander chatbot for slack")
7 | parser.add_argument('--test', '-t', dest='test', action='store_true', required=False,
8 | help='Enter command line mode to enter a charmander repl')
9 | parser.add_argument('--hook',dest='hook', action='store', default='message',
10 | help='Specify the hook to test. (Defaults to "message")')
11 | parser.add_argument('-c', dest="command", help='run a single command')
12 | parser.add_argument('--database', '-d', dest='database_name', default='charmander.sqlite3',
13 | help="Where to store the charmander sqlite database. Defaults to charmander.sqlite")
14 | parser.add_argument('--pluginpath', '-pp', dest='pluginpath', default=None,
15 | help="The path where charmander should look to find its plugins")
16 | args = parser.parse_args()
17 |
18 | main(args)
--------------------------------------------------------------------------------
/charmander/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = 'vikesh'
2 |
--------------------------------------------------------------------------------
/charmander/charmander.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from __future__ import print_function # why? Read here : http://python3porting.com/noconv.html
4 | import copy # read here : https://docs.python.org/2/library/copy.html
5 | import traceback # read : https://docs.python.org/2/library/traceback.html
6 | import sqlite3 # read : https://docs.python.org/2/library/sqlite3.html
7 | import logging # read : https://docs.python.org/2/library/logging.html
8 | import os # read : https://docs.python.org/2/library/os.html
9 | import functools # read : https://docs.python.org/2/library/functools.html
10 | import re # read : https://docs.python.org/2/library/re.html
11 | import sys # read : https://docs.python.org/2/library/sys.html
12 | import importlib
13 | import ipdb
14 | from glob import glob
15 |
16 |
17 | from server import CharmanderServer
18 | from dummyserver import DummyServer
19 |
20 | from slackrtm import SlackClient
21 | from slackrtm.server import SlackConnectionError, SlackLoginError
22 |
23 |
24 | CURRENT_DIR = os.path.abspath(os.path.dirname(__file__))
25 | DIR = functools.partial(os.path.join,CURRENT_DIR)
26 |
27 | PYTHON_3 = sys.version_info[0] > 2
28 |
29 | logger = logging.getLogger(__name__)
30 |
31 |
32 | class InvalidExtensionDir(Exception):
33 | def __int__(self, extensiondir):
34 | '''
35 | :param extensiondir: check if the extension directory is valid
36 | :return:
37 | '''
38 | message = "Unable to find extension directory {0}".format(extensiondir)
39 | super(InvalidExtensionDir, self).__init__(message)
40 |
41 |
42 | def init_log(config):
43 | '''
44 | :param config: log configuration
45 | :return:
46 | '''
47 | loglevel = config.get("loglevel", logging.INFO)
48 | logformat = config.get("logformat", '%(asctime)s:%(levelname)s:%(name)s:%(message)s')
49 | if config.get("logfile"):
50 | logging.basicConfig(filename=config.get("logfile"), format=logformat, level=loglevel)
51 | else:
52 | logging.basicConfig(format=logformat, level=loglevel)
53 |
54 |
55 | def init_extensions(extensiondir):
56 | '''
57 | :param extensiondir: get all the extensions (python files) also check for errors
58 | :return:
59 | '''
60 | if extensiondir and not os.path.isdir(extensiondir):
61 | raise InvalidExtensionDir(extensiondir)
62 |
63 | if not extensiondir:
64 | extensiondir = DIR("extensions")
65 |
66 | logger.debug("externsiondir: {0}".format(extensiondir))
67 |
68 | if os.path.isdir(extensiondir):
69 | extensionfiles = glob(os.path.join(extensiondir, "[!_]*.py"))
70 | extensions = strip_extension(os.path.basename(e) for e in extensionfiles )
71 | else:
72 |
73 | logger.debug("trying to get pkg_resources")
74 | import pkg_resources
75 | try:
76 | extensions = strip_extension(
77 | pkg_resources.resource_listdir(__name__, "extensions"))
78 | except OSError:
79 | raise InvalidExtensionDir(extensiondir)
80 |
81 | hooks = {}
82 |
83 | old_path = copy.deepcopy(sys.path)
84 | sys.path.insert(0,extensiondir)
85 |
86 | for extension in extensions:
87 | logger.debug("extension: {0}".format(extension))
88 | try:
89 | mod = importlib.import_module(extension)
90 | modname = mod.__name__
91 | for hook in re.findall("on_(\w+)"," ".join(dir(mod))):
92 | hook_fun = getattr(mod, "on_"+hook)
93 | logger.debug("extension : attching %s hook for %s ", hook, modname)
94 | hooks.setdefault(hook, []).append(hook_fun)
95 |
96 | if mod.__doc__:
97 | firstline = mod.__doc__.split('\n')[0]
98 | hooks.setdefault('help', {})[modname] = firstline
99 | hooks.setdefault('extension Help', {})[modname] = mod.__doc__
100 | # we will have exntensions , they can have any number of errors
101 | # we have to make sure that they don't affect our servers
102 | except:
103 | logger.warning("import failed on module {0}, module not loaded".format(extension))
104 | logger.warning("{0}".format(sys.exc_info()[0]))
105 | logger.warning("{0}".format(traceback.format_exc()))
106 |
107 | sys.path = old_path
108 | return hooks
109 |
110 |
111 | def strip_extension(list):
112 | '''
113 | :param list: Get list and strip the list
114 | :return:
115 | '''
116 | return (os.path.splitext(l)[0] for l in list)
117 |
118 |
119 | def run_hook(hooks, hook, *args):
120 | '''
121 | :param hooks: Get hooks and loop through it
122 | :param hook:
123 | :param args:
124 | :return: return responses
125 | '''
126 | responses = []
127 | for hook in hooks.get(hook, []):
128 | try:
129 | h = hook(*args)
130 | if h:
131 | responses.append(h)
132 | except:
133 | logger.warning("Failed to run the extension {0}, module not loaded".format(hook))
134 | logger.warning("{0}".format(sys.exc_info()[0]))
135 | logger.warning("{0}".format(traceback.format_exc()))
136 |
137 | return responses
138 |
139 |
140 | def handle_bot_message(event, server):
141 | '''
142 | :param event: Handle bot events
143 | :param server:
144 | :return:
145 | '''
146 | try:
147 | bot = server.slack.server.bots[event["bot_id"]]
148 | except KeyError:
149 | logger.debug('bot_meesage event {0} has no bot'.format(event))
150 | return
151 | return "\n".join(run_hook(server.hooks, "bot_message", event, server))
152 |
153 |
154 | def handle_message(event, server):
155 | # Bot message handling
156 | subtype = event.get("subtype", "")
157 | if subtype == "message_changed":
158 | return
159 |
160 | if subtype == "bot_message":
161 | return handle_bot_message(event, server)
162 |
163 | try:
164 | msg_user = server.slack.server.users[event["user"]]
165 | except KeyError:
166 | logger.debug("event {0} has no user".format(event))
167 | return
168 |
169 | return "\n".join(run_hook(server.hooks, "message", event, server))
170 |
171 | event_handlers = {
172 | "message": handle_message,
173 | }
174 |
175 |
176 | def handle_event(event, server):
177 | # Event Handling
178 | handler = event_handlers.get(event.get("type"))
179 | if handler:
180 | return handler(event, server)
181 |
182 |
183 | def getif(config, name, envvar):
184 | if envvar in os.environ:
185 | config[name] = os.environ.get(envvar)
186 |
187 |
188 | def init_config():
189 | # get and initialize all the configurations
190 | config = {}
191 | getif(config, "token", "SLACK_TOKEN")
192 | getif(config, "loglevel", "CHARMANDER_LOGLEVEL")
193 | getif(config, "logfile", "CHARMANDER_LOGFILE")
194 | getif(config, "logformat", "CHARMANDER_LOGFORMAT")
195 | return config
196 |
197 |
198 | def loop(server , test_loop=None):
199 | """
200 | :param server: is a charmander server object
201 | :param test_loop: is the number of times to run the loop
202 | :return:
203 | """
204 | try:
205 | loop_without_activity = 0
206 | while test_loop is None or test_loop > 0:
207 | start = time.time()
208 | loop_without_activity += 1
209 |
210 | events = server.slack.rtm_read()
211 | for event in events:
212 | loop_without_activity = 0
213 | logger.debug("got {0}".format(event.get("type", event)))
214 | response = handle_event(event, server)
215 | if response:
216 | server.slack.rtm_send_message(event["channel"], response)
217 |
218 | # Server.slack.post_message to send messages from a loop hook
219 | run_hook(server.hooks, "loop", server)
220 | # The Slack RTM API docs say:
221 | #
222 | # > When there is no other activity clients should send a ping
223 | # > every few seconds
224 | #
225 | # So, if we've gone >5 seconds without any activity, send a ping.
226 | # If the connection has broken, this will reveal it so slack can
227 | # quit
228 | if loop_without_activity > 5:
229 | server.slack.server.ping()
230 | loop_without_activity = 0
231 |
232 | end = time.time()
233 | runtime = start - end
234 | time.sleep(max(1-runtime, 0))
235 |
236 | if test_loop:
237 | test_loop -= 1
238 | except KeyboardInterrupt:
239 | if os.environ.get("CHARMANDER_DEBUG"):
240 | import ipdb; ipdb.set_trace()
241 | raise
242 |
243 |
244 | def relevant_environ():
245 | return dict((key, os.environ[key])
246 | for key in os.environ
247 | if key.startswith("SLACK") or key.startswith("CHARMANDER"))
248 |
249 |
250 | def init_server(args, config, Server=CharmanderServer, Client=SlackClient):
251 | # Initialize Server using SlackClient
252 |
253 | init_log(config)
254 | logger.debug("config: {0}".format(config))
255 | db = init_db(args.database_name)
256 | hooks = init_extensions(args.extensionpath)
257 | try:
258 | slack = Client(config["token"])
259 | except KeyError:
260 | logger.error(""" Charmander is unable to find a slack token. The environment variables charmander sees are:
261 | {0} and the current config is: {1}
262 | """.format(relevant_environ(), config))
263 | raise
264 | server = Server(slack, config, hooks, db)
265 | return server
266 |
267 |
268 | def decode(str_, codec='utf-8'):
269 | # Decode string
270 | if PYTHON_3:
271 | return str_
272 | else:
273 | return str_.decode(codec)
274 |
275 |
276 | def encode(str_, codec='utf-8'):
277 | # encode string
278 | if PYTHON_3:
279 | return str_
280 | else:
281 | return str_.encode(codec)
282 |
283 |
284 | def main(args):
285 | config = init_config()
286 | if args.test:
287 | init_log(config)
288 | return repl(DummyServer(), args)
289 | elif args.command is not None:
290 | init_log(config)
291 | cmd = decode(args.command)
292 | print(run_cmd(cmd, DummyServer(), args.hook, args.extensionpath))
293 |
294 | server = init_server(args, config)
295 |
296 | try:
297 | server.slack.rtm_connect()
298 |
299 | run_hook(server.hooks, "init", server)
300 |
301 | loop(server)
302 | except SlackConnectionError:
303 | logger.warn("Unable to connect to slack. Bad Gateway? :( ")
304 | raise
305 | except SlackLoginError:
306 | logger.warn("Login Failed, invalid token <{0}>".format(config["token"]))
307 |
308 |
309 | def run_cmd(cmd, server, hook, extennsionpath):
310 | # command line handling
311 | server.hooks = init_extensions(extennsionpath)
312 | event = {'type': hook, 'text': cmd, "user": "2", "ts": time.time(), "team": None, "channel": "repl_channel"}
313 | return encode(handle_event(event, server))
314 |
315 |
316 | try:
317 | input = raw_input
318 | except NameError:
319 | pass
320 |
321 |
322 | def repl(server, args):
323 | try:
324 | while 1:
325 | cmd = decode(input("charmander> "))
326 | if cmd.lower() == "quit" or cmd.lower() == "exit":
327 | return
328 |
329 | print(run_cmd(cmd, server, args.hook, args.extensionpath))
330 | except (EOFError, KeyboardInterrupt):
331 | print()
332 | pass
333 |
334 |
335 | def init_db(database_file):
336 | # initialize db
337 | return sqlite3.connect(database_file)
--------------------------------------------------------------------------------
/charmander/dummyserver.py:
--------------------------------------------------------------------------------
1 | __author__ = 'vikesh'
2 |
3 | from slackrtm.server import User, Bot
4 |
5 |
6 | class DummyServer(object):
7 | def __init__(self, slack=None, config=None, hooks=None, db=None):
8 | self.slack = slack or DummySlack()
9 | self.config = config
10 | self.hooks = hooks
11 | self.db = db
12 |
13 | def query(self, sql, *params):
14 | if not self.db:
15 | return None
16 |
17 | cursor = self.db.cursor()
18 | cursor.execute(sql, params)
19 | rows = cursor.fetchall()
20 | cursor.close()
21 | self.db.commit()
22 | return rows
23 |
24 |
25 | class DummySlack(object):
26 | def __init__(self, server=None, users=None, events=None):
27 | self.server = server or DummySlackServer(users=users)
28 | self.posted_messages = None
29 | self.events = events if events else []
30 |
31 | def post_message(self, message, **kwargs):
32 | self.posted_messages = (message, kwargs)
33 |
34 | def rtm_read(self):
35 | return self.events.pop() if self.events else []
36 |
37 |
38 | class DummySlackServer(object):
39 | def __init__(self, botname="Charmander", users=None, bots=None):
40 | self.login_data = {
41 | "self": {
42 | "name": botname,
43 | }
44 | }
45 | self.username = "replbot"
46 |
47 | self.users = users if users else {
48 | "1": User(self, "charmander_test", 1, "", 0),
49 | "2": User(self, "vickyuser", 2, "", 0),
50 | "3": User(self, "slackbot", 3, "", 0),
51 | }
52 |
53 | self.bots = bots if bots else {
54 | "1": Bot("1", "otherbot", [], False)
55 | }
56 |
--------------------------------------------------------------------------------
/charmander/extensions/__init__.py:
--------------------------------------------------------------------------------
1 | __author__ = 'vikesh'
2 |
--------------------------------------------------------------------------------
/charmander/extensions/calc.py:
--------------------------------------------------------------------------------
1 | __author__ = 'vikesh'
2 |
3 | " " " calc will return the google calculator result for " " "
4 |
5 | from bs4 import BeautifulSoup
6 | import re
7 |
8 | try:
9 | from urllib import quote
10 | except ImportError:
11 | from urllib2 import quote
12 | import requests
13 |
14 |
15 | def calc(eq):
16 | query = quote(eq)
17 | url = "https://encrypted.google.com/search?hl=en&q={0}".format(query)
18 | soup = BeautifulSoup(requests.get(url).text, "html5lib")
19 |
20 | answer = soup.findAll("h2", attrs={"class": "r"})
21 | if not answer:
22 | answer = soup.findAll("span", attrs={"class": "_m3b"})
23 | if not answer:
24 | return ":crying_cat_face: sorry, google doesn't have an answer for this equation :("
25 |
26 | answer = answer[0].text.replace(u"\xa0", ",")
27 | return answer
28 |
29 |
30 | def on_message(msg, server):
31 | text = msg.get("text", "")
32 | match = re.findall(r"~calc (.*)", text)
33 | if not match:
34 | return
35 |
36 | return calc(match[0].encode("utf8"))
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/charmander/extensions/commit.py:
--------------------------------------------------------------------------------
1 | __author__ = 'vikesh'
2 |
3 | # Get random commit messages
4 | """ ~commit returns random commit messages"""
5 |
6 | import re
7 | import requests
8 |
9 |
10 | def commit():
11 | return requests.get("http://whatthecommit.com/index.txt").text
12 |
13 |
14 | def on_message(msg, server):
15 | text = msg.get("text", "")
16 | match = re.findall(r"~commit( .*)?", text)
17 |
18 | if not match:
19 | return
20 |
21 | return commit()
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/charmander/extensions/emoji.py:
--------------------------------------------------------------------------------
1 | __author__ = 'vikesh'
2 | """
3 | <~emoji> N : This will return N random emojis!
4 | """
5 |
6 | import re
7 | import random
8 | from emojidic import emojiDict
9 |
10 |
11 | def randomelect(dic):
12 | keys = list(dic.keys())
13 | i = random.randint(0, len(keys)-1) # get random emoji
14 | return dic[keys[i]]
15 |
16 |
17 | def emoji(n=1):
18 | emoji = []
19 | for i in range(n):
20 | emoji.append(randomelect(emojiDict))
21 |
22 | return "".join(emoji)
23 |
24 |
25 | def on_message(msg, server):
26 | text = msg.get("text", "")
27 | match = re.findall(r"(~emoji)\s*(\d+)*", text)
28 | if not match:
29 | return
30 |
31 | N = 1 if not match[0][1] else int(match[0][1])
32 |
33 | return emoji(N)
34 |
35 |
36 |
--------------------------------------------------------------------------------
/charmander/extensions/emojidic.py:
--------------------------------------------------------------------------------
1 | #
2 | # This file is based on emoji (https://github.com/kyokomi/emoji).
3 | #
4 | # The MIT License (MIT)
5 | #
6 | # Copyright (c) 2014 kyokomi
7 | #
8 | # Permission is hereby granted, free of charge, to any person obtaining a copy
9 | # of this software and associated documentation files (the "Software"), to deal
10 | # in the Software without restriction, including without limitation the rights
11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | # copies of the Software, and to permit persons to whom the Software is
13 | # furnished to do so, subject to the following conditions:
14 | #
15 | # The above copyright notice and this permission notice shall be included in all
16 | # copies or substantial portions of the Software.
17 | #
18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 | # SOFTWARE.
25 | #
26 |
27 | emojiDict = {
28 | ":capricorn:": u"\U00002651",
29 | ":end:": u"\U0001f51a",
30 | ":no_mobile_phones:": u"\U0001f4f5",
31 | ":couple:": u"\U0001f46b",
32 | ":snowman:": u"\U000026c4",
33 | ":sunrise_over_mountains:": u"\U0001f304",
34 | ":suspension_railway:": u"\U0001f69f",
35 | ":arrows_counterclockwise:": u"\U0001f504",
36 | ":bug:": u"\U0001f41b",
37 | ":confused:": u"\U0001f615",
38 | ":dress:": u"\U0001f457",
39 | ":honeybee:": u"\U0001f41d",
40 | ":waning_crescent_moon:": u"\U0001f318",
41 | ":balloon:": u"\U0001f388",
42 | ":bus:": u"\U0001f68c",
43 | ":package:": u"\U0001f4e6",
44 | ":pencil2:": u"\U0000270f",
45 | ":rage:": u"\U0001f621",
46 | ":space_invader:": u"\U0001f47e",
47 | ":white_medium_small_square:": u"\U000025fd",
48 | ":fast_forward:": u"\U000023e9",
49 | ":rice_cracker:": u"\U0001f358",
50 | ":incoming_envelope:": u"\U0001f4e8",
51 | ":sa:": u"\U0001f202",
52 | ":womens:": u"\U0001f6ba",
53 | ":arrow_right:": u"\U000027a1",
54 | ":construction_worker:": u"\U0001f477",
55 | ":notes:": u"\U0001f3b6",
56 | ":goat:": u"\U0001f410",
57 | ":grey_question:": u"\U00002754",
58 | ":lantern:": u"\U0001f3ee",
59 | ":rice_scene:": u"\U0001f391",
60 | ":running:": u"\U0001f3c3",
61 | ":ferris_wheel:": u"\U0001f3a1",
62 | ":musical_score:": u"\U0001f3bc",
63 | ":sparkle:": u"\U00002747",
64 | ":wink:": u"\U0001f609",
65 | ":art:": u"\U0001f3a8",
66 | ":clock330:": u"\U0001f55e",
67 | ":minidisc:": u"\U0001f4bd",
68 | ":no_entry_sign:": u"\U0001f6ab",
69 | ":wind_chime:": u"\U0001f390",
70 | ":cyclone:": u"\U0001f300",
71 | ":herb:": u"\U0001f33f",
72 | ":leopard:": u"\U0001f406",
73 | ":banana:": u"\U0001f34c",
74 | ":handbag:": u"\U0001f45c",
75 | ":honey_pot:": u"\U0001f36f",
76 | ":ok:": u"\U0001f197",
77 | ":hearts:": u"\U00002665",
78 | ":passport_control:": u"\U0001f6c2",
79 | ":moyai:": u"\U0001f5ff",
80 | ":smile:": u"\U0001f604",
81 | ":tiger2:": u"\U0001f405",
82 | ":twisted_rightwards_arrows:": u"\U0001f500",
83 | ":children_crossing:": u"\U0001f6b8",
84 | ":cow:": u"\U0001f42e",
85 | ":point_up:": u"\U0000261d",
86 | ":house:": u"\U0001f3e0",
87 | ":man_with_turban:": u"\U0001f473",
88 | ":mountain_railway:": u"\U0001f69e",
89 | ":vibration_mode:": u"\U0001f4f3",
90 | ":blowfish:": u"\U0001f421",
91 | ":it:": u"\U0001f1ee\U0001f1f9",
92 | ":oden:": u"\U0001f362",
93 | ":clock3:": u"\U0001f552",
94 | ":lollipop:": u"\U0001f36d",
95 | ":train:": u"\U0001f68b",
96 | ":scissors:": u"\U00002702",
97 | ":triangular_ruler:": u"\U0001f4d0",
98 | ":wedding:": u"\U0001f492",
99 | ":flashlight:": u"\U0001f526",
100 | ":secret:": u"\U00003299",
101 | ":sushi:": u"\U0001f363",
102 | ":blue_car:": u"\U0001f699",
103 | ":cd:": u"\U0001f4bf",
104 | ":milky_way:": u"\U0001f30c",
105 | ":mortar_board:": u"\U0001f393",
106 | ":crown:": u"\U0001f451",
107 | ":speech_balloon:": u"\U0001f4ac",
108 | ":bento:": u"\U0001f371",
109 | ":grey_exclamation:": u"\U00002755",
110 | ":hotel:": u"\U0001f3e8",
111 | ":keycap_ten:": u"\U0001f51f",
112 | ":newspaper:": u"\U0001f4f0",
113 | ":outbox_tray:": u"\U0001f4e4",
114 | ":racehorse:": u"\U0001f40e",
115 | ":laughing:": u"\U0001f606",
116 | ":black_large_square:": u"\U00002b1b",
117 | ":books:": u"\U0001f4da",
118 | ":eight_spoked_asterisk:": u"\U00002733",
119 | ":heavy_check_mark:": u"\U00002714",
120 | ":m:": u"\U000024c2",
121 | ":wave:": u"\U0001f44b",
122 | ":bicyclist:": u"\U0001f6b4",
123 | ":cocktail:": u"\U0001f378",
124 | ":european_castle:": u"\U0001f3f0",
125 | ":point_down:": u"\U0001f447",
126 | ":tokyo_tower:": u"\U0001f5fc",
127 | ":battery:": u"\U0001f50b",
128 | ":dancer:": u"\U0001f483",
129 | ":repeat:": u"\U0001f501",
130 | ":ru:": u"\U0001f1f7\U0001f1fa",
131 | ":new_moon:": u"\U0001f311",
132 | ":church:": u"\U000026ea",
133 | ":date:": u"\U0001f4c5",
134 | ":earth_americas:": u"\U0001f30e",
135 | ":footprints:": u"\U0001f463",
136 | ":libra:": u"\U0000264e",
137 | ":mountain_cableway:": u"\U0001f6a0",
138 | ":small_red_triangle_down:": u"\U0001f53b",
139 | ":top:": u"\U0001f51d",
140 | ":sunglasses:": u"\U0001f60e",
141 | ":abcd:": u"\U0001f521",
142 | ":cl:": u"\U0001f191",
143 | ":ski:": u"\U0001f3bf",
144 | ":book:": u"\U0001f4d6",
145 | ":hourglass_flowing_sand:": u"\U000023f3",
146 | ":stuck_out_tongue_closed_eyes:": u"\U0001f61d",
147 | ":cold_sweat:": u"\U0001f630",
148 | ":headphones:": u"\U0001f3a7",
149 | ":confetti_ball:": u"\U0001f38a",
150 | ":gemini:": u"\U0000264a",
151 | ":new:": u"\U0001f195",
152 | ":pray:": u"\U0001f64f",
153 | ":watch:": u"\U0000231a",
154 | ":coffee:": u"\U00002615",
155 | ":ghost:": u"\U0001f47b",
156 | ":on:": u"\U0001f51b",
157 | ":pouch:": u"\U0001f45d",
158 | ":taxi:": u"\U0001f695",
159 | ":hocho:": u"\U0001f52a",
160 | ":yum:": u"\U0001f60b",
161 | ":heavy_plus_sign:": u"\U00002795",
162 | ":tada:": u"\U0001f389",
163 | ":arrow_heading_down:": u"\U00002935",
164 | ":clock530:": u"\U0001f560",
165 | ":poultry_leg:": u"\U0001f357",
166 | ":elephant:": u"\U0001f418",
167 | ":gb:": u"\U0001f1ec\U0001f1e7",
168 | ":mahjong:": u"\U0001f004",
169 | ":rice:": u"\U0001f35a",
170 | ":musical_note:": u"\U0001f3b5",
171 | ":beginner:": u"\U0001f530",
172 | ":small_red_triangle:": u"\U0001f53a",
173 | ":tomato:": u"\U0001f345",
174 | ":clock1130:": u"\U0001f566",
175 | ":japanese_castle:": u"\U0001f3ef",
176 | ":sun_with_face:": u"\U0001f31e",
177 | ":four:": u"\U00000034\U000020e3",
178 | ":microphone:": u"\U0001f3a4",
179 | ":tennis:": u"\U0001f3be",
180 | ":arrow_up_down:": u"\U00002195",
181 | ":cn:": u"\U0001f1e8\U0001f1f3",
182 | ":horse_racing:": u"\U0001f3c7",
183 | ":no_bicycles:": u"\U0001f6b3",
184 | ":snail:": u"\U0001f40c",
185 | ":free:": u"\U0001f193",
186 | ":beetle:": u"\U0001f41e",
187 | ":black_small_square:": u"\U000025aa",
188 | ":file_folder:": u"\U0001f4c1",
189 | ":hushed:": u"\U0001f62f",
190 | ":skull:": u"\U0001f480",
191 | ":ab:": u"\U0001f18e",
192 | ":rocket:": u"\U0001f680",
193 | ":sweet_potato:": u"\U0001f360",
194 | ":guitar:": u"\U0001f3b8",
195 | ":poodle:": u"\U0001f429",
196 | ":tulip:": u"\U0001f337",
197 | ":large_orange_diamond:": u"\U0001f536",
198 | ":-1:": u"\U0001f44e",
199 | ":chart_with_upwards_trend:": u"\U0001f4c8",
200 | ":de:": u"\U0001f1e9\U0001f1ea",
201 | ":grapes:": u"\U0001f347",
202 | ":ideograph_advantage:": u"\U0001f250",
203 | ":japanese_ogre:": u"\U0001f479",
204 | ":telephone:": u"\U0000260e",
205 | ":clock230:": u"\U0001f55d",
206 | ":hourglass:": u"\U0000231b",
207 | ":leftwards_arrow_with_hook:": u"\U000021a9",
208 | ":sparkler:": u"\U0001f387",
209 | ":black_joker:": u"\U0001f0cf",
210 | ":clock730:": u"\U0001f562",
211 | ":first_quarter_moon_with_face:": u"\U0001f31b",
212 | ":man:": u"\U0001f468",
213 | ":clock4:": u"\U0001f553",
214 | ":fishing_pole_and_fish:": u"\U0001f3a3",
215 | ":tophat:": u"\U0001f3a9",
216 | ":white_medium_square:": u"\U000025fb",
217 | ":mega:": u"\U0001f4e3",
218 | ":spaghetti:": u"\U0001f35d",
219 | ":dart:": u"\U0001f3af",
220 | ":girl:": u"\U0001f467",
221 | ":womans_hat:": u"\U0001f452",
222 | ":bullettrain_front:": u"\U0001f685",
223 | ":department_store:": u"\U0001f3ec",
224 | ":heartbeat:": u"\U0001f493",
225 | ":palm_tree:": u"\U0001f334",
226 | ":swimmer:": u"\U0001f3ca",
227 | ":yellow_heart:": u"\U0001f49b",
228 | ":arrow_upper_right:": u"\U00002197",
229 | ":clock2:": u"\U0001f551",
230 | ":high_heel:": u"\U0001f460",
231 | ":arrow_double_up:": u"\U000023eb",
232 | ":cry:": u"\U0001f622",
233 | ":dvd:": u"\U0001f4c0",
234 | ":e-mail:": u"\U0001f4e7",
235 | ":baby_bottle:": u"\U0001f37c",
236 | ":cool:": u"\U0001f192",
237 | ":floppy_disk:": u"\U0001f4be",
238 | ":iphone:": u"\U0001f4f1",
239 | ":minibus:": u"\U0001f690",
240 | ":rooster:": u"\U0001f413",
241 | ":three:": u"\U00000033\U000020e3",
242 | ":white_small_square:": u"\U000025ab",
243 | ":cancer:": u"\U0000264b",
244 | ":question:": u"\U00002753",
245 | ":sake:": u"\U0001f376",
246 | ":birthday:": u"\U0001f382",
247 | ":dog2:": u"\U0001f415",
248 | ":loudspeaker:": u"\U0001f4e2",
249 | ":arrow_up_small:": u"\U0001f53c",
250 | ":camel:": u"\U0001f42b",
251 | ":koala:": u"\U0001f428",
252 | ":mag_right:": u"\U0001f50e",
253 | ":soccer:": u"\U000026bd",
254 | ":bike:": u"\U0001f6b2",
255 | ":ear_of_rice:": u"\U0001f33e",
256 | ":shit:": u"\U0001f4a9",
257 | ":u7981:": u"\U0001f232",
258 | ":bath:": u"\U0001f6c0",
259 | ":baby:": u"\U0001f476",
260 | ":lock_with_ink_pen:": u"\U0001f50f",
261 | ":necktie:": u"\U0001f454",
262 | ":bikini:": u"\U0001f459",
263 | ":blush:": u"\U0001f60a",
264 | ":heartpulse:": u"\U0001f497",
265 | ":pig_nose:": u"\U0001f43d",
266 | ":straight_ruler:": u"\U0001f4cf",
267 | ":u6e80:": u"\U0001f235",
268 | ":gift:": u"\U0001f381",
269 | ":traffic_light:": u"\U0001f6a5",
270 | ":hibiscus:": u"\U0001f33a",
271 | ":couple_with_heart:": u"\U0001f491",
272 | ":pushpin:": u"\U0001f4cc",
273 | ":u6709:": u"\U0001f236",
274 | ":walking:": u"\U0001f6b6",
275 | ":grinning:": u"\U0001f600",
276 | ":hash:": u"\U00000023\U000020e3",
277 | ":radio_button:": u"\U0001f518",
278 | ":raised_hand:": u"\U0000270b",
279 | ":shaved_ice:": u"\U0001f367",
280 | ":barber:": u"\U0001f488",
281 | ":cat:": u"\U0001f431",
282 | ":heavy_exclamation_mark:": u"\U00002757",
283 | ":ice_cream:": u"\U0001f368",
284 | ":mask:": u"\U0001f637",
285 | ":pig2:": u"\U0001f416",
286 | ":triangular_flag_on_post:": u"\U0001f6a9",
287 | ":arrow_upper_left:": u"\U00002196",
288 | ":bee:": u"\U0001f41d",
289 | ":beer:": u"\U0001f37a",
290 | ":black_nib:": u"\U00002712",
291 | ":exclamation:": u"\U00002757",
292 | ":dog:": u"\U0001f436",
293 | ":fire:": u"\U0001f525",
294 | ":ant:": u"\U0001f41c",
295 | ":broken_heart:": u"\U0001f494",
296 | ":chart:": u"\U0001f4b9",
297 | ":clock1:": u"\U0001f550",
298 | ":bomb:": u"\U0001f4a3",
299 | ":virgo:": u"\U0000264d",
300 | ":a:": u"\U0001f170",
301 | ":fork_and_knife:": u"\U0001f374",
302 | ":copyright:": u"\U000000a9",
303 | ":curly_loop:": u"\U000027b0",
304 | ":full_moon:": u"\U0001f315",
305 | ":shoe:": u"\U0001f45e",
306 | ":european_post_office:": u"\U0001f3e4",
307 | ":ng:": u"\U0001f196",
308 | ":office:": u"\U0001f3e2",
309 | ":raising_hand:": u"\U0001f64b",
310 | ":revolving_hearts:": u"\U0001f49e",
311 | ":aquarius:": u"\U00002652",
312 | ":electric_plug:": u"\U0001f50c",
313 | ":meat_on_bone:": u"\U0001f356",
314 | ":mens:": u"\U0001f6b9",
315 | ":briefcase:": u"\U0001f4bc",
316 | ":ship:": u"\U0001f6a2",
317 | ":anchor:": u"\U00002693",
318 | ":ballot_box_with_check:": u"\U00002611",
319 | ":bear:": u"\U0001f43b",
320 | ":beers:": u"\U0001f37b",
321 | ":dromedary_camel:": u"\U0001f42a",
322 | ":nut_and_bolt:": u"\U0001f529",
323 | ":construction:": u"\U0001f6a7",
324 | ":golf:": u"\U000026f3",
325 | ":toilet:": u"\U0001f6bd",
326 | ":blue_book:": u"\U0001f4d8",
327 | ":boom:": u"\U0001f4a5",
328 | ":deciduous_tree:": u"\U0001f333",
329 | ":kissing_closed_eyes:": u"\U0001f61a",
330 | ":smiley_cat:": u"\U0001f63a",
331 | ":fuelpump:": u"\U000026fd",
332 | ":kiss:": u"\U0001f48b",
333 | ":clock10:": u"\U0001f559",
334 | ":sheep:": u"\U0001f411",
335 | ":white_flower:": u"\U0001f4ae",
336 | ":boar:": u"\U0001f417",
337 | ":currency_exchange:": u"\U0001f4b1",
338 | ":facepunch:": u"\U0001f44a",
339 | ":flower_playing_cards:": u"\U0001f3b4",
340 | ":person_frowning:": u"\U0001f64d",
341 | ":poop:": u"\U0001f4a9",
342 | ":satisfied:": u"\U0001f606",
343 | ":8ball:": u"\U0001f3b1",
344 | ":disappointed_relieved:": u"\U0001f625",
345 | ":panda_face:": u"\U0001f43c",
346 | ":ticket:": u"\U0001f3ab",
347 | ":us:": u"\U0001f1fa\U0001f1f8",
348 | ":waxing_crescent_moon:": u"\U0001f312",
349 | ":dragon:": u"\U0001f409",
350 | ":gun:": u"\U0001f52b",
351 | ":mount_fuji:": u"\U0001f5fb",
352 | ":new_moon_with_face:": u"\U0001f31a",
353 | ":star2:": u"\U0001f31f",
354 | ":grimacing:": u"\U0001f62c",
355 | ":confounded:": u"\U0001f616",
356 | ":congratulations:": u"\U00003297",
357 | ":custard:": u"\U0001f36e",
358 | ":frowning:": u"\U0001f626",
359 | ":maple_leaf:": u"\U0001f341",
360 | ":police_car:": u"\U0001f693",
361 | ":cloud:": u"\U00002601",
362 | ":jeans:": u"\U0001f456",
363 | ":fish:": u"\U0001f41f",
364 | ":wavy_dash:": u"\U00003030",
365 | ":clock5:": u"\U0001f554",
366 | ":santa:": u"\U0001f385",
367 | ":japan:": u"\U0001f5fe",
368 | ":oncoming_taxi:": u"\U0001f696",
369 | ":whale:": u"\U0001f433",
370 | ":arrow_forward:": u"\U000025b6",
371 | ":kissing_heart:": u"\U0001f618",
372 | ":bullettrain_side:": u"\U0001f684",
373 | ":fearful:": u"\U0001f628",
374 | ":moneybag:": u"\U0001f4b0",
375 | ":runner:": u"\U0001f3c3",
376 | ":mailbox:": u"\U0001f4eb",
377 | ":sandal:": u"\U0001f461",
378 | ":zzz:": u"\U0001f4a4",
379 | ":apple:": u"\U0001f34e",
380 | ":arrow_heading_up:": u"\U00002934",
381 | ":family:": u"\U0001f46a",
382 | ":heavy_minus_sign:": u"\U00002796",
383 | ":saxophone:": u"\U0001f3b7",
384 | ":u5272:": u"\U0001f239",
385 | ":black_square_button:": u"\U0001f532",
386 | ":bouquet:": u"\U0001f490",
387 | ":love_letter:": u"\U0001f48c",
388 | ":metro:": u"\U0001f687",
389 | ":small_blue_diamond:": u"\U0001f539",
390 | ":thought_balloon:": u"\U0001f4ad",
391 | ":arrow_up:": u"\U00002b06",
392 | ":no_pedestrians:": u"\U0001f6b7",
393 | ":smirk:": u"\U0001f60f",
394 | ":blue_heart:": u"\U0001f499",
395 | ":large_blue_diamond:": u"\U0001f537",
396 | ":vs:": u"\U0001f19a",
397 | ":v:": u"\U0000270c",
398 | ":wheelchair:": u"\U0000267f",
399 | ":couplekiss:": u"\U0001f48f",
400 | ":tent:": u"\U000026fa",
401 | ":purple_heart:": u"\U0001f49c",
402 | ":relaxed:": u"\U0000263a",
403 | ":accept:": u"\U0001f251",
404 | ":green_heart:": u"\U0001f49a",
405 | ":pouting_cat:": u"\U0001f63e",
406 | ":tram:": u"\U0001f68a",
407 | ":bangbang:": u"\U0000203c",
408 | ":collision:": u"\U0001f4a5",
409 | ":convenience_store:": u"\U0001f3ea",
410 | ":person_with_blond_hair:": u"\U0001f471",
411 | ":uk:": u"\U0001f1ec\U0001f1e7",
412 | ":peach:": u"\U0001f351",
413 | ":tired_face:": u"\U0001f62b",
414 | ":bread:": u"\U0001f35e",
415 | ":mailbox_closed:": u"\U0001f4ea",
416 | ":open_mouth:": u"\U0001f62e",
417 | ":pig:": u"\U0001f437",
418 | ":put_litter_in_its_place:": u"\U0001f6ae",
419 | ":u7a7a:": u"\U0001f233",
420 | ":bulb:": u"\U0001f4a1",
421 | ":clock9:": u"\U0001f558",
422 | ":envelope_with_arrow:": u"\U0001f4e9",
423 | ":pisces:": u"\U00002653",
424 | ":baggage_claim:": u"\U0001f6c4",
425 | ":egg:": u"\U0001f373",
426 | ":sweat_smile:": u"\U0001f605",
427 | ":boat:": u"\U000026f5",
428 | ":fr:": u"\U0001f1eb\U0001f1f7",
429 | ":heavy_division_sign:": u"\U00002797",
430 | ":muscle:": u"\U0001f4aa",
431 | ":paw_prints:": u"\U0001f43e",
432 | ":arrow_left:": u"\U00002b05",
433 | ":black_circle:": u"\U000026ab",
434 | ":kissing_smiling_eyes:": u"\U0001f619",
435 | ":star:": u"\U00002b50",
436 | ":steam_locomotive:": u"\U0001f682",
437 | ":1234:": u"\U0001f522",
438 | ":clock130:": u"\U0001f55c",
439 | ":kr:": u"\U0001f1f0\U0001f1f7",
440 | ":monorail:": u"\U0001f69d",
441 | ":school:": u"\U0001f3eb",
442 | ":seven:": u"\U00000037\U000020e3",
443 | ":baby_chick:": u"\U0001f424",
444 | ":bridge_at_night:": u"\U0001f309",
445 | ":hotsprings:": u"\U00002668",
446 | ":rose:": u"\U0001f339",
447 | ":love_hotel:": u"\U0001f3e9",
448 | ":princess:": u"\U0001f478",
449 | ":ramen:": u"\U0001f35c",
450 | ":scroll:": u"\U0001f4dc",
451 | ":tropical_fish:": u"\U0001f420",
452 | ":heart_eyes_cat:": u"\U0001f63b",
453 | ":information_desk_person:": u"\U0001f481",
454 | ":mouse:": u"\U0001f42d",
455 | ":no_smoking:": u"\U0001f6ad",
456 | ":post_office:": u"\U0001f3e3",
457 | ":stars:": u"\U0001f320",
458 | ":arrow_double_down:": u"\U000023ec",
459 | ":unlock:": u"\U0001f513",
460 | ":arrow_backward:": u"\U000025c0",
461 | ":hand:": u"\U0000270b",
462 | ":hospital:": u"\U0001f3e5",
463 | ":ocean:": u"\U0001f30a",
464 | ":mountain_bicyclist:": u"\U0001f6b5",
465 | ":octopus:": u"\U0001f419",
466 | ":sos:": u"\U0001f198",
467 | ":dizzy_face:": u"\U0001f635",
468 | ":tongue:": u"\U0001f445",
469 | ":train2:": u"\U0001f686",
470 | ":checkered_flag:": u"\U0001f3c1",
471 | ":orange_book:": u"\U0001f4d9",
472 | ":sound:": u"\U0001f509",
473 | ":aerial_tramway:": u"\U0001f6a1",
474 | ":bell:": u"\U0001f514",
475 | ":dragon_face:": u"\U0001f432",
476 | ":flipper:": u"\U0001f42c",
477 | ":ok_woman:": u"\U0001f646",
478 | ":performing_arts:": u"\U0001f3ad",
479 | ":postal_horn:": u"\U0001f4ef",
480 | ":clock1030:": u"\U0001f565",
481 | ":email:": u"\U00002709",
482 | ":green_book:": u"\U0001f4d7",
483 | ":point_up_2:": u"\U0001f446",
484 | ":high_brightness:": u"\U0001f506",
485 | ":running_shirt_with_sash:": u"\U0001f3bd",
486 | ":bookmark:": u"\U0001f516",
487 | ":sob:": u"\U0001f62d",
488 | ":arrow_lower_right:": u"\U00002198",
489 | ":point_left:": u"\U0001f448",
490 | ":purse:": u"\U0001f45b",
491 | ":sparkles:": u"\U00002728",
492 | ":black_medium_small_square:": u"\U000025fe",
493 | ":pound:": u"\U0001f4b7",
494 | ":rabbit:": u"\U0001f430",
495 | ":woman:": u"\U0001f469",
496 | ":negative_squared_cross_mark:": u"\U0000274e",
497 | ":open_book:": u"\U0001f4d6",
498 | ":smiling_imp:": u"\U0001f608",
499 | ":spades:": u"\U00002660",
500 | ":baseball:": u"\U000026be",
501 | ":fountain:": u"\U000026f2",
502 | ":joy:": u"\U0001f602",
503 | ":lipstick:": u"\U0001f484",
504 | ":partly_sunny:": u"\U000026c5",
505 | ":ram:": u"\U0001f40f",
506 | ":red_circle:": u"\U0001f534",
507 | ":cop:": u"\U0001f46e",
508 | ":green_apple:": u"\U0001f34f",
509 | ":registered:": u"\U000000ae",
510 | ":+1:": u"\U0001f44d",
511 | ":crying_cat_face:": u"\U0001f63f",
512 | ":innocent:": u"\U0001f607",
513 | ":mobile_phone_off:": u"\U0001f4f4",
514 | ":underage:": u"\U0001f51e",
515 | ":dolphin:": u"\U0001f42c",
516 | ":busts_in_silhouette:": u"\U0001f465",
517 | ":umbrella:": u"\U00002614",
518 | ":angel:": u"\U0001f47c",
519 | ":small_orange_diamond:": u"\U0001f538",
520 | ":sunflower:": u"\U0001f33b",
521 | ":link:": u"\U0001f517",
522 | ":notebook:": u"\U0001f4d3",
523 | ":oncoming_bus:": u"\U0001f68d",
524 | ":bookmark_tabs:": u"\U0001f4d1",
525 | ":calendar:": u"\U0001f4c6",
526 | ":izakaya_lantern:": u"\U0001f3ee",
527 | ":mans_shoe:": u"\U0001f45e",
528 | ":name_badge:": u"\U0001f4db",
529 | ":closed_lock_with_key:": u"\U0001f510",
530 | ":fist:": u"\U0000270a",
531 | ":id:": u"\U0001f194",
532 | ":ambulance:": u"\U0001f691",
533 | ":musical_keyboard:": u"\U0001f3b9",
534 | ":ribbon:": u"\U0001f380",
535 | ":seedling:": u"\U0001f331",
536 | ":tv:": u"\U0001f4fa",
537 | ":football:": u"\U0001f3c8",
538 | ":nail_care:": u"\U0001f485",
539 | ":seat:": u"\U0001f4ba",
540 | ":alarm_clock:": u"\U000023f0",
541 | ":money_with_wings:": u"\U0001f4b8",
542 | ":relieved:": u"\U0001f60c",
543 | ":womans_clothes:": u"\U0001f45a",
544 | ":lips:": u"\U0001f444",
545 | ":clubs:": u"\U00002663",
546 | ":house_with_garden:": u"\U0001f3e1",
547 | ":sunrise:": u"\U0001f305",
548 | ":monkey:": u"\U0001f412",
549 | ":six:": u"\U00000036\U000020e3",
550 | ":smiley:": u"\U0001f603",
551 | ":feet:": u"\U0001f43e",
552 | ":waning_gibbous_moon:": u"\U0001f316",
553 | ":yen:": u"\U0001f4b4",
554 | ":baby_symbol:": u"\U0001f6bc",
555 | ":signal_strength:": u"\U0001f4f6",
556 | ":boy:": u"\U0001f466",
557 | ":busstop:": u"\U0001f68f",
558 | ":computer:": u"\U0001f4bb",
559 | ":night_with_stars:": u"\U0001f303",
560 | ":older_woman:": u"\U0001f475",
561 | ":parking:": u"\U0001f17f",
562 | ":trumpet:": u"\U0001f3ba",
563 | ":100:": u"\U0001f4af",
564 | ":sweat_drops:": u"\U0001f4a6",
565 | ":wc:": u"\U0001f6be",
566 | ":b:": u"\U0001f171",
567 | ":cupid:": u"\U0001f498",
568 | ":five:": u"\U00000035\U000020e3",
569 | ":part_alternation_mark:": u"\U0000303d",
570 | ":snowboarder:": u"\U0001f3c2",
571 | ":warning:": u"\U000026a0",
572 | ":white_large_square:": u"\U00002b1c",
573 | ":zap:": u"\U000026a1",
574 | ":arrow_down_small:": u"\U0001f53d",
575 | ":clock430:": u"\U0001f55f",
576 | ":expressionless:": u"\U0001f611",
577 | ":phone:": u"\U0000260e",
578 | ":roller_coaster:": u"\U0001f3a2",
579 | ":lemon:": u"\U0001f34b",
580 | ":one:": u"\U00000031\U000020e3",
581 | ":christmas_tree:": u"\U0001f384",
582 | ":hankey:": u"\U0001f4a9",
583 | ":hatched_chick:": u"\U0001f425",
584 | ":u7533:": u"\U0001f238",
585 | ":large_blue_circle:": u"\U0001f535",
586 | ":up:": u"\U0001f199",
587 | ":wine_glass:": u"\U0001f377",
588 | ":x:": u"\U0000274c",
589 | ":nose:": u"\U0001f443",
590 | ":rewind:": u"\U000023ea",
591 | ":two_hearts:": u"\U0001f495",
592 | ":envelope:": u"\U00002709",
593 | ":oncoming_automobile:": u"\U0001f698",
594 | ":ophiuchus:": u"\U000026ce",
595 | ":ring:": u"\U0001f48d",
596 | ":tropical_drink:": u"\U0001f379",
597 | ":turtle:": u"\U0001f422",
598 | ":crescent_moon:": u"\U0001f319",
599 | ":koko:": u"\U0001f201",
600 | ":microscope:": u"\U0001f52c",
601 | ":rugby_football:": u"\U0001f3c9",
602 | ":smoking:": u"\U0001f6ac",
603 | ":anger:": u"\U0001f4a2",
604 | ":aries:": u"\U00002648",
605 | ":city_sunset:": u"\U0001f306",
606 | ":clock1230:": u"\U0001f567",
607 | ":mailbox_with_no_mail:": u"\U0001f4ed",
608 | ":movie_camera:": u"\U0001f3a5",
609 | ":pager:": u"\U0001f4df",
610 | ":zero:": u"\U00000030\U000020e3",
611 | ":bank:": u"\U0001f3e6",
612 | ":eight_pointed_black_star:": u"\U00002734",
613 | ":knife:": u"\U0001f52a",
614 | ":u7121:": u"\U0001f21a",
615 | ":customs:": u"\U0001f6c3",
616 | ":melon:": u"\U0001f348",
617 | ":rowboat:": u"\U0001f6a3",
618 | ":corn:": u"\U0001f33d",
619 | ":eggplant:": u"\U0001f346",
620 | ":heart_decoration:": u"\U0001f49f",
621 | ":rotating_light:": u"\U0001f6a8",
622 | ":round_pushpin:": u"\U0001f4cd",
623 | ":cat2:": u"\U0001f408",
624 | ":chocolate_bar:": u"\U0001f36b",
625 | ":no_bell:": u"\U0001f515",
626 | ":radio:": u"\U0001f4fb",
627 | ":droplet:": u"\U0001f4a7",
628 | ":hamburger:": u"\U0001f354",
629 | ":fire_engine:": u"\U0001f692",
630 | ":heart:": u"\U00002764",
631 | ":potable_water:": u"\U0001f6b0",
632 | ":telephone_receiver:": u"\U0001f4de",
633 | ":dash:": u"\U0001f4a8",
634 | ":globe_with_meridians:": u"\U0001f310",
635 | ":guardsman:": u"\U0001f482",
636 | ":heavy_multiplication_x:": u"\U00002716",
637 | ":chart_with_downwards_trend:": u"\U0001f4c9",
638 | ":imp:": u"\U0001f47f",
639 | ":earth_asia:": u"\U0001f30f",
640 | ":mouse2:": u"\U0001f401",
641 | ":notebook_with_decorative_cover:": u"\U0001f4d4",
642 | ":telescope:": u"\U0001f52d",
643 | ":trolleybus:": u"\U0001f68e",
644 | ":card_index:": u"\U0001f4c7",
645 | ":euro:": u"\U0001f4b6",
646 | ":dollar:": u"\U0001f4b5",
647 | ":fax:": u"\U0001f4e0",
648 | ":mailbox_with_mail:": u"\U0001f4ec",
649 | ":raised_hands:": u"\U0001f64c",
650 | ":disappointed:": u"\U0001f61e",
651 | ":foggy:": u"\U0001f301",
652 | ":person_with_pouting_face:": u"\U0001f64e",
653 | ":statue_of_liberty:": u"\U0001f5fd",
654 | ":dolls:": u"\U0001f38e",
655 | ":light_rail:": u"\U0001f688",
656 | ":pencil:": u"\U0001f4dd",
657 | ":speak_no_evil:": u"\U0001f64a",
658 | ":calling:": u"\U0001f4f2",
659 | ":clock830:": u"\U0001f563",
660 | ":cow2:": u"\U0001f404",
661 | ":hear_no_evil:": u"\U0001f649",
662 | ":scream_cat:": u"\U0001f640",
663 | ":smile_cat:": u"\U0001f638",
664 | ":tractor:": u"\U0001f69c",
665 | ":clock11:": u"\U0001f55a",
666 | ":doughnut:": u"\U0001f369",
667 | ":hammer:": u"\U0001f528",
668 | ":loop:": u"\U000027bf",
669 | ":moon:": u"\U0001f314",
670 | ":soon:": u"\U0001f51c",
671 | ":cinema:": u"\U0001f3a6",
672 | ":factory:": u"\U0001f3ed",
673 | ":flushed:": u"\U0001f633",
674 | ":mute:": u"\U0001f507",
675 | ":neutral_face:": u"\U0001f610",
676 | ":scorpius:": u"\U0000264f",
677 | ":wolf:": u"\U0001f43a",
678 | ":clapper:": u"\U0001f3ac",
679 | ":joy_cat:": u"\U0001f639",
680 | ":pensive:": u"\U0001f614",
681 | ":sleeping:": u"\U0001f634",
682 | ":credit_card:": u"\U0001f4b3",
683 | ":leo:": u"\U0000264c",
684 | ":man_with_gua_pi_mao:": u"\U0001f472",
685 | ":open_hands:": u"\U0001f450",
686 | ":tea:": u"\U0001f375",
687 | ":arrow_down:": u"\U00002b07",
688 | ":nine:": u"\U00000039\U000020e3",
689 | ":punch:": u"\U0001f44a",
690 | ":slot_machine:": u"\U0001f3b0",
691 | ":clap:": u"\U0001f44f",
692 | ":information_source:": u"\U00002139",
693 | ":tiger:": u"\U0001f42f",
694 | ":city_sunrise:": u"\U0001f307",
695 | ":dango:": u"\U0001f361",
696 | ":thumbsdown:": u"\U0001f44e",
697 | ":u6307:": u"\U0001f22f",
698 | ":curry:": u"\U0001f35b",
699 | ":cherries:": u"\U0001f352",
700 | ":clock6:": u"\U0001f555",
701 | ":clock7:": u"\U0001f556",
702 | ":older_man:": u"\U0001f474",
703 | ":oncoming_police_car:": u"\U0001f694",
704 | ":syringe:": u"\U0001f489",
705 | ":heavy_dollar_sign:": u"\U0001f4b2",
706 | ":open_file_folder:": u"\U0001f4c2",
707 | ":arrow_right_hook:": u"\U000021aa",
708 | ":articulated_lorry:": u"\U0001f69b",
709 | ":dancers:": u"\U0001f46f",
710 | ":kissing_cat:": u"\U0001f63d",
711 | ":rainbow:": u"\U0001f308",
712 | ":u5408:": u"\U0001f234",
713 | ":boot:": u"\U0001f462",
714 | ":carousel_horse:": u"\U0001f3a0",
715 | ":fried_shrimp:": u"\U0001f364",
716 | ":lock:": u"\U0001f512",
717 | ":non-potable_water:": u"\U0001f6b1",
718 | ":o:": u"\U00002b55",
719 | ":persevere:": u"\U0001f623",
720 | ":diamond_shape_with_a_dot_inside:": u"\U0001f4a0",
721 | ":fallen_leaf:": u"\U0001f342",
722 | ":massage:": u"\U0001f486",
723 | ":volcano:": u"\U0001f30b",
724 | ":gem:": u"\U0001f48e",
725 | ":shower:": u"\U0001f6bf",
726 | ":speaker:": u"\U0001f508",
727 | ":last_quarter_moon_with_face:": u"\U0001f31c",
728 | ":mag:": u"\U0001f50d",
729 | ":anguished:": u"\U0001f627",
730 | ":monkey_face:": u"\U0001f435",
731 | ":sunny:": u"\U00002600",
732 | ":tangerine:": u"\U0001f34a",
733 | ":point_right:": u"\U0001f449",
734 | ":railway_car:": u"\U0001f683",
735 | ":triumph:": u"\U0001f624",
736 | ":two:": u"\U00000032\U000020e3",
737 | ":gift_heart:": u"\U0001f49d",
738 | ":ledger:": u"\U0001f4d2",
739 | ":sagittarius:": u"\U00002650",
740 | ":snowflake:": u"\U00002744",
741 | ":abc:": u"\U0001f524",
742 | ":horse:": u"\U0001f434",
743 | ":ok_hand:": u"\U0001f44c",
744 | ":video_camera:": u"\U0001f4f9",
745 | ":sparkling_heart:": u"\U0001f496",
746 | ":taurus:": u"\U00002649",
747 | ":frog:": u"\U0001f438",
748 | ":hamster:": u"\U0001f439",
749 | ":helicopter:": u"\U0001f681",
750 | ":fries:": u"\U0001f35f",
751 | ":mushroom:": u"\U0001f344",
752 | ":penguin:": u"\U0001f427",
753 | ":truck:": u"\U0001f69a",
754 | ":bar_chart:": u"\U0001f4ca",
755 | ":evergreen_tree:": u"\U0001f332",
756 | ":bow:": u"\U0001f647",
757 | ":clock12:": u"\U0001f55b",
758 | ":four_leaf_clover:": u"\U0001f340",
759 | ":inbox_tray:": u"\U0001f4e5",
760 | ":smirk_cat:": u"\U0001f63c",
761 | ":two_men_holding_hands:": u"\U0001f46c",
762 | ":water_buffalo:": u"\U0001f403",
763 | ":alien:": u"\U0001f47d",
764 | ":video_game:": u"\U0001f3ae",
765 | ":candy:": u"\U0001f36c",
766 | ":page_facing_up:": u"\U0001f4c4",
767 | ":watermelon:": u"\U0001f349",
768 | ":white_check_mark:": u"\U00002705",
769 | ":blossom:": u"\U0001f33c",
770 | ":crocodile:": u"\U0001f40a",
771 | ":no_mouth:": u"\U0001f636",
772 | ":o2:": u"\U0001f17e",
773 | ":shirt:": u"\U0001f455",
774 | ":clock8:": u"\U0001f557",
775 | ":eyes:": u"\U0001f440",
776 | ":rabbit2:": u"\U0001f407",
777 | ":tanabata_tree:": u"\U0001f38b",
778 | ":wrench:": u"\U0001f527",
779 | ":es:": u"\U0001f1ea\U0001f1f8",
780 | ":trophy:": u"\U0001f3c6",
781 | ":two_women_holding_hands:": u"\U0001f46d",
782 | ":clock630:": u"\U0001f561",
783 | ":pineapple:": u"\U0001f34d",
784 | ":stuck_out_tongue:": u"\U0001f61b",
785 | ":angry:": u"\U0001f620",
786 | ":athletic_shoe:": u"\U0001f45f",
787 | ":cookie:": u"\U0001f36a",
788 | ":flags:": u"\U0001f38f",
789 | ":game_die:": u"\U0001f3b2",
790 | ":bird:": u"\U0001f426",
791 | ":jack_o_lantern:": u"\U0001f383",
792 | ":ox:": u"\U0001f402",
793 | ":paperclip:": u"\U0001f4ce",
794 | ":sleepy:": u"\U0001f62a",
795 | ":astonished:": u"\U0001f632",
796 | ":back:": u"\U0001f519",
797 | ":closed_book:": u"\U0001f4d5",
798 | ":hatching_chick:": u"\U0001f423",
799 | ":arrows_clockwise:": u"\U0001f503",
800 | ":car:": u"\U0001f697",
801 | ":ear:": u"\U0001f442",
802 | ":haircut:": u"\U0001f487",
803 | ":icecream:": u"\U0001f366",
804 | ":bust_in_silhouette:": u"\U0001f464",
805 | ":diamonds:": u"\U00002666",
806 | ":no_good:": u"\U0001f645",
807 | ":pizza:": u"\U0001f355",
808 | ":chicken:": u"\U0001f414",
809 | ":eyeglasses:": u"\U0001f453",
810 | ":see_no_evil:": u"\U0001f648",
811 | ":earth_africa:": u"\U0001f30d",
812 | ":fireworks:": u"\U0001f386",
813 | ":page_with_curl:": u"\U0001f4c3",
814 | ":rice_ball:": u"\U0001f359",
815 | ":white_square_button:": u"\U0001f533",
816 | ":cake:": u"\U0001f370",
817 | ":red_car:": u"\U0001f697",
818 | ":tm:": u"\U00002122",
819 | ":unamused:": u"\U0001f612",
820 | ":fish_cake:": u"\U0001f365",
821 | ":key:": u"\U0001f511",
822 | ":speedboat:": u"\U0001f6a4",
823 | ":closed_umbrella:": u"\U0001f302",
824 | ":pear:": u"\U0001f350",
825 | ":satellite:": u"\U0001f4e1",
826 | ":scream:": u"\U0001f631",
827 | ":first_quarter_moon:": u"\U0001f313",
828 | ":jp:": u"\U0001f1ef\U0001f1f5",
829 | ":repeat_one:": u"\U0001f502",
830 | ":shell:": u"\U0001f41a",
831 | ":interrobang:": u"\U00002049",
832 | ":trident:": u"\U0001f531",
833 | ":u55b6:": u"\U0001f23a",
834 | ":atm:": u"\U0001f3e7",
835 | ":door:": u"\U0001f6aa",
836 | ":kissing:": u"\U0001f617",
837 | ":six_pointed_star:": u"\U0001f52f",
838 | ":thumbsup:": u"\U0001f44d",
839 | ":u6708:": u"\U0001f237",
840 | ":do_not_litter:": u"\U0001f6af",
841 | ":whale2:": u"\U0001f40b",
842 | ":school_satchel:": u"\U0001f392",
843 | ":cactus:": u"\U0001f335",
844 | ":clipboard:": u"\U0001f4cb",
845 | ":dizzy:": u"\U0001f4ab",
846 | ":waxing_gibbous_moon:": u"\U0001f314",
847 | ":camera:": u"\U0001f4f7",
848 | ":capital_abcd:": u"\U0001f520",
849 | ":leaves:": u"\U0001f343",
850 | ":left_luggage:": u"\U0001f6c5",
851 | ":bamboo:": u"\U0001f38d",
852 | ":bowling:": u"\U0001f3b3",
853 | ":eight:": u"\U00000038\U000020e3",
854 | ":kimono:": u"\U0001f458",
855 | ":left_right_arrow:": u"\U00002194",
856 | ":stuck_out_tongue_winking_eye:": u"\U0001f61c",
857 | ":surfer:": u"\U0001f3c4",
858 | ":sweat:": u"\U0001f613",
859 | ":violin:": u"\U0001f3bb",
860 | ":postbox:": u"\U0001f4ee",
861 | ":bride_with_veil:": u"\U0001f470",
862 | ":recycle:": u"\U0000267b",
863 | ":station:": u"\U0001f689",
864 | ":vhs:": u"\U0001f4fc",
865 | ":crossed_flags:": u"\U0001f38c",
866 | ":memo:": u"\U0001f4dd",
867 | ":no_entry:": u"\U000026d4",
868 | ":white_circle:": u"\U000026aa",
869 | ":arrow_lower_left:": u"\U00002199",
870 | ":chestnut:": u"\U0001f330",
871 | ":crystal_ball:": u"\U0001f52e",
872 | ":last_quarter_moon:": u"\U0001f317",
873 | ":loud_sound:": u"\U0001f50a",
874 | ":strawberry:": u"\U0001f353",
875 | ":worried:": u"\U0001f61f",
876 | ":circus_tent:": u"\U0001f3aa",
877 | ":weary:": u"\U0001f629",
878 | ":bathtub:": u"\U0001f6c1",
879 | ":snake:": u"\U0001f40d",
880 | ":grin:": u"\U0001f601",
881 | ":symbols:": u"\U0001f523",
882 | ":airplane:": u"\U00002708",
883 | ":heart_eyes:": u"\U0001f60d",
884 | ":sailboat:": u"\U000026f5",
885 | ":stew:": u"\U0001f372",
886 | ":tshirt:": u"\U0001f455",
887 | ":rat:": u"\U0001f400",
888 | ":black_medium_square:": u"\U000025fc",
889 | ":clock930:": u"\U0001f564",
890 | ":full_moon_with_face:": u"\U0001f31d",
891 | ":japanese_goblin:": u"\U0001f47a",
892 | ":restroom:": u"\U0001f6bb",
893 | ":vertical_traffic_light:": u"\U0001f6a6",
894 | ":basketball:": u"\U0001f3c0",
895 | ":cherry_blossom:": u"\U0001f338",
896 | ":low_brightness:": u"\U0001f505",
897 | ":pill:": u"\U0001f48a",
898 | }
899 |
--------------------------------------------------------------------------------
/charmander/extensions/google.py:
--------------------------------------------------------------------------------
1 | """ ~search will return the top google result for that query """
2 |
3 | from bs4 import BeautifulSoup
4 | import re
5 | import requests
6 |
7 | __author__ = 'vikesh'
8 |
9 | try:
10 | from urllib import quote, unquote
11 | except ImportError:
12 | from urllib.request import quote, unquote
13 |
14 |
15 | def google(query):
16 | query = quote(query)
17 | url = "https://encrypted.google.com/search?q={0}".format(query)
18 | soup = BeautifulSoup(requests.get(url).text, "html5lib")
19 |
20 | answer = soup.findAll("h3", attrs={"class": "r"})
21 | if not answer:
22 | return ":fire: Sorry, Google doesn't have an answer for your Query :fire:"
23 |
24 | return unquote(re.findall(r"q=(.*?)&", str(answer[0]))[0])
25 |
26 |
27 | def message(msg, server):
28 | text = msg.get("text", "")
29 | match = re.findall(r"~(?:google|search) (.*)", text)
30 |
31 | if not match:
32 | return
33 |
34 | return google(match[0])
35 |
--------------------------------------------------------------------------------
/charmander/extensions/help.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 |
4 | def on_message(msg,server):
5 | text = msg.get("text","")
6 | match = re.findall(r"~help( .*)?", text)
7 | if not match:
8 | return
9 |
10 |
11 | help_topic = match[0].strip()
12 | if help_topic:
13 | return server.hooks["extendedhelp"].get(help_topic,
14 | ":fire: no help found for {0}".format(help_topic))
15 | else:
16 | help_dir = server.hooks.get("help",{})
17 | return "\n".join(sorted(help_dir[key]) for key in help_dir)
18 |
--------------------------------------------------------------------------------
/charmander/extensions/log.py:
--------------------------------------------------------------------------------
1 | """
2 | Log all messages to the database only active
3 | If the CHARMANDER_LOG environment variable is set.
4 | """
5 |
6 | import os
7 |
8 | DO_LOG = os.environ.get("CHARMANDER_LOG", False)
9 |
10 |
11 | def on_message(msg, server):
12 | if DO_LOG:
13 | server.query("INSERT INTO log VALUES (?, ?, ?, ?)",
14 | msg["text"], msg["user"], msg["ts"], msg["team"], msg["channel"])
15 |
16 |
17 | def on_init_(server):
18 | if DO_LOG:
19 | server.query("""
20 | CREATE TABLE IF NOT EXISTS log (msg STRING, sender STRING, time STRING, team STRING, channel STRING)
21 | """)
--------------------------------------------------------------------------------
/charmander/extensions/urban.py:
--------------------------------------------------------------------------------
1 | # -*- coding: UTF-8 -*-
2 | # urban returns the urban dictionary definition and example of a term
3 |
4 | import requests
5 | import re
6 |
7 | try:
8 | from urllib import quote
9 | except ImportError:
10 | from urllib.request import quote
11 |
12 |
13 | def reply_to(string):
14 | lines = string.split("\n")
15 | return "\n".join("> _{0}_".format(l.strip()) for l in lines)
16 |
17 |
18 | def urban(term):
19 |
20 | # Slack replaces the quote character with a smart quote.
21 | # Undo that
22 | term = term.replace(u'’', "'").encode("utf8")
23 |
24 | baseurl = u"http://api.urbandictionary.com/v0/define?term={0}"
25 | data = requests.get(baseurl.format(quote(term))).json()
26 |
27 | try:
28 | result = data['list'][0]
29 | result["example"] = reply_to(result.get("example", ""))
30 | definition = (u"*{word}*: {definition}.\n"
31 | "*Example:*\n{example}".format(**result))
32 |
33 | return definition
34 | except IndexError:
35 | return ":boom: No results found for {0}, please try again".format(term)
36 |
37 |
38 | def on_message(msg, server):
39 | text = msg.get("text", "")
40 | match = re.findall(r"~urban (.*)", text)
41 | if not match:
42 | return
43 | searchterm = match[0]
44 | return urban(searchterm)
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/charmander/extensions/wiki.py:
--------------------------------------------------------------------------------
1 | __author__ = 'vikesh'
2 |
3 | """
4 | ~wiki returns a wiki link for that
5 | """
6 |
7 | import re
8 | try:
9 | from urllib import quote
10 | except ImportError:
11 | from urllib2.request import quote
12 |
13 |
14 | import requests
15 | from bs4 import BeautifulSoup
16 |
17 |
18 | def return_wiki(search_term):
19 | search_term = quote(search_term)
20 |
21 | url = "https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch={0}&format=json"
22 | url = url.format(search_term)
23 |
24 | result = requests.get(url).json()
25 | total_pages = result["query"]["search"]
26 |
27 | total_pages = [p for p in total_pages if 'may refer to' not in p["snippets"]]
28 |
29 | if not total_pages:
30 | return ""
31 |
32 | total_pages = quote(total_pages[0]["title"].encode("utf-8"))
33 |
34 | link = "http://en.wikipedia.org/wiki/{0}".format(total_pages)
35 |
36 | res = requests.get("https://en.wikipedia.org/w/api.php?format=json&action=parse&page={0}".format(total_pages)).json()
37 |
38 | soup = BeautifulSoup(res["parse"]["text"]["*"], "html5lib")
39 | p = soup.find('p').get_text()
40 | p = p[:8000]
41 |
42 | return u"{0}\n{1}".format(p, link)
43 |
44 |
45 | def on_message(msg, server):
46 | text = msg.get("text","")
47 | match = re.findall(r"~wiki (.*)", text)
48 | if not match:
49 | return
50 |
51 | search_term = match[0]
52 | return return_wiki(search_term.encode("utf-8"))
--------------------------------------------------------------------------------
/charmander/extensions/youtube.py:
--------------------------------------------------------------------------------
1 | __author__ = 'vikesh'
2 |
3 | """ ~youtube will return the first youtube search result for the given term. """
4 |
5 | import re
6 |
7 | try:
8 | from urllib import quote
9 | except ImportError:
10 | from urllib.request import quote
11 |
12 | import requests
13 |
14 |
15 | def youtube(searchquery):
16 | url = "https://www.youtube.com/results?search_query={0}"
17 | url = url.format(quote(searchquery))
18 |
19 | r = requests.get(url)
20 | results = re.findall('a href="(/watch[^&]*?)"', r.text)
21 |
22 | if not results:
23 | return "Sorry, charmander couldn't find any videos :crying_cat:"
24 |
25 | return "https://www.youtube.com{0}".format(results[0])
26 |
27 |
28 | def on_message(msg, server):
29 | text = msg.get("text", "")
30 | match = re.findall(r"~youtube (.*)", text)
31 |
32 | if not match:
33 | return
34 |
35 | searchquery = match[0]
36 | return youtube(searchquery.encode("utf8"))
37 |
--------------------------------------------------------------------------------
/charmander/server.py:
--------------------------------------------------------------------------------
1 | __author__ = 'vikesh'
2 |
3 | class CharmanderServer(object):
4 | def __init__(self, slack, config, hooks, db):
5 | self.slack = slack
6 | self.config = config
7 | self.hooks = hooks
8 | self.db = db
9 |
10 | def query(self, sql, *params):
11 | curser = self.db.cursor()
12 | curser.execute(sql, params)
13 | rows = curser.fetchall()
14 | curser.close()
15 | self.db.commit()
16 | return rows
--------------------------------------------------------------------------------
/output_ZrAcxK.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vicky002/charmander/33a91e9a7b508d8106ba1644e9bec386d1f50eb4/output_ZrAcxK.gif
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | html5lib==0.9999999
2 | pyflakes==0.8.1
3 | beautifulsoup4==4.4.1
4 | flake8==2.3.0
5 | slackrtm==0.2.1
6 | requests==2.5.2
7 | websocket-client==0.32.0
8 | mccabe==0.3
9 | wrapt==1.10.2
10 | ipdb
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | # Read more her : http://flake8.readthedocs.org/en/latest/
3 | # And error codes here : http://pep8.readthedocs.org/en/latest/intro.html#error-codes
4 | ignore = E302,E241
5 | max-line-length = 120
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | __author__ = 'vikesh'
2 |
3 | import os
4 | import sys
5 |
6 |
7 | try:
8 | from setuptools import setup
9 | except ImportError:
10 | from distutils.core import setup
11 |
12 |
13 | PYTHON3 = sys.version_info[0] > 2
14 |
15 | required = ['requests>=2.5', 'websocket-client==0.32.0',
16 | 'beautifulsoup4==4.4.1', 'html5lib==0.9999999', 'pyfiglet==0.7.4',
17 | 'slackrtm==0.2.1']
18 |
19 | if not PYTHON3:
20 | required += ['importlib>=1.0.3']
21 |
22 | packages = []
23 |
24 | try:
25 | longdesc = open("README.rs").read()
26 | except:
27 | longdesc = ''
28 |
29 | # Thanks to Kenneth Reitz, I stole the template for this : https://github.com/kennethreitz
30 | setup(
31 | name='charmander',
32 | version='1.0.0',
33 | description='An Intelligent bot for slack',
34 | long_description=longdesc,
35 | author='Vikesh Tiwari',
36 | author_email='hi@eulercoder.me',
37 | url='https://github.com/vicky002/Charmander',
38 | packages=packages,
39 | scripts= ['bin/charmander'],
40 | package_data = {'': ['LICENCE',], '':['charmander/extensions/*.py']},
41 | include_package_data=True,
42 | install_requires=required,
43 | license='MIT',
44 | classifiers=(
45 | 'Development Status :: 1 - Beta',
46 | 'Intended Audience :: Developers',
47 | 'Natural Language :: English',
48 | 'License :: OSI Approved :: MIT License',
49 | 'Programming Language :: Python',
50 | 'Programming Language :: Python :: 2.6',
51 | 'Programming Language :: Python :: 2.7',
52 | 'Programming Language :: Python :: 3.4',
53 | ),
54 |
55 | )
--------------------------------------------------------------------------------