├── .gitignore
├── .replit
├── CHANGELOG.md
├── Dockerfile
├── LICENSE
├── README.md
├── animations
├── helloworld.txt
└── rick.txt
├── banner.png
├── customrs.gif
├── docker-compose.yml
├── example.gif
├── requirements.txt
├── slacky.gif
├── slacky
├── __init__.py
├── __main__.py
├── api
│ ├── __init__.py
│ └── auth.py
├── config
│ ├── __init__.py
│ └── load.py
├── constants
│ ├── __init__.py
│ └── emojis.py
├── helpers
│ └── __init__.py
└── plugins
│ ├── __init__.py
│ └── custom
│ ├── __init__.py
│ ├── deepfry
│ ├── __init__.py
│ ├── deepfrylogic.py
│ ├── flare.png
│ ├── haarcascade_eye.xml
│ ├── haarcascade_frontalface_default.xml
│ └── plugin.py
│ ├── example.py
│ └── stockpic
│ ├── __init__.py
│ └── plugin.py
└── version.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | config.json
2 | **/*__pycache__
3 | **/*.pyc
4 | *.json
5 | **/*wormy.txt
6 | **/*wormy2.txt
7 | Dockerfile.apptapp
8 | Dockerfile.reggora
9 | max.yml
--------------------------------------------------------------------------------
/.replit:
--------------------------------------------------------------------------------
1 | language = "python3"
2 | run = "python3 -m slacky"
3 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### Update 1.8.3:
2 |
3 | - Adds update check in bot and `stats` command.
4 |
5 | ### Update 1.8.2:
6 |
7 | - Fix `setstatus` command from being `status` command
8 |
9 | ### Update 1.8.1:
10 |
11 | - Fix requirements to include `pandas`
12 |
13 | ### Update 1.8:
14 |
15 | - Adds `coronastatus` command to check Wuhan Coronavirus Status
16 |
17 | ### Update 1.7:
18 |
19 | - Adds `stockpic` command.
20 | - Requires Shutterstock Credentials
21 |
22 | ### Update 1.6:
23 |
24 | - Adds `deepfry` command from forked [deeppyer](https://github.com/Ovyerus/deeppyer)
25 | - `deepfry` is a custom command and must be loaded in `plugins/__init__.py` manually. Requires `OpenCV 4`.
26 |
27 | ## Update 1.5.5:
28 |
29 | - Fix `ping` command
30 |
31 | ### Update 1.5.4:
32 |
33 | - Fix `help` with `ping` command
34 |
35 | ### Update 1.5.3:
36 |
37 | - New format for `ping` and `stats` command
38 |
39 | ### Update 1.5.2:
40 |
41 | - Fixes `heartbeat` command and adds `ping` command
42 | - New `help` command and subcommands
43 |
44 | ### Update 1.5.1:
45 |
46 | - Merges @TotallyChase's PR
47 | - Performance and Optimization Boost
48 | - Plugin loading refactor
49 | - Adds `msgstatus` command
50 |
51 | ### Update 1.5:
52 |
53 | - Huge refactor with `cmd_setup` function
54 | - Error Handling
55 |
56 | ### Update 1.4.6:
57 |
58 | - Fix `winfo` formatting
59 |
60 | ### Update 1.4.5:
61 |
62 | - Fix `listener` command
63 |
64 | ### Update 1.4.4:
65 |
66 | - Update `help` command
67 | - Better Error Handling on Custom Replies
68 |
69 | ### Update 1.4.3:
70 |
71 | - Uses attachments to make replies prettier
72 | - Fixes `ani` command breaking
73 |
74 | ### Update 1.4.2:
75 |
76 | - Fix slowing bug due to hitting `winfo` each time
77 |
78 | ### Update 1.4.1:
79 |
80 | - Update requirements.txt
81 |
82 | ### Update 1.4:
83 |
84 | - New Plugin Loading
85 | - Error Handling
86 | - Adds `winfo`, `convinfo`, and `uinfo` commands
87 |
88 | ### Update 1.3.2:
89 |
90 | - More Error Handling
91 | - Adds `rick` animation as default
92 | - Adds looping argument to `ani` command. New syntax `ani [ani_name] [loop count]`
93 |
94 | ### Update 1.3.1:
95 |
96 | - Hotfix for `errors` command formatting
97 |
98 | ### Update 1.3:
99 |
100 | - Adds `animations` and the `ani` command. Read Wiki for more info on animations.
101 | - New bots error handling and `errors` command to display errors triggered
102 |
103 | ### Update 1.2.2:
104 |
105 | - Fix emoji handling in the `react` command
106 |
107 | ### Update 1.2.1:
108 |
109 | - Fix `customrs` command
110 | - Regex sucks!
111 |
112 | ### Update 1.2:
113 |
114 | - Add `stats, space, ud` commands.
115 | - Clean up more error handling
116 | - Adds BotMetaData class for bot data like uptime, commands run, errors, etc
117 |
118 | ### Update 1.1.4:
119 |
120 | - Fix Custom Replies
121 | - More Error Handling
122 |
123 | ### Update 1.1.3:
124 |
125 | - Better Error Handling
126 | - Fix some broken commands
127 |
128 | ### Update 1.1.2:
129 |
130 | - Clean Up Code a Bit
131 |
132 | ### Update 1.1.1:
133 |
134 | - Adds Custom Config Creation/Selection Argument
135 | - Error Handling Updates for Ctrl+C on Config Wizard
136 |
137 | ### Update 1.1:
138 |
139 | - Adds Custom Replies. Read the [Wiki](https://github.com/M4cs/Slacky/wiki) to learn how to use them.
140 | - Bug fixes
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.7.4
2 | MAINTAINER Max Bridgland
3 |
4 | RUN mkdir -p /usr/src/slacky
5 | WORKDIR /usr/src/app
6 |
7 | ADD requirements.txt /usr/src/app/requirements.txt
8 |
9 | RUN pip install -r requirements.txt
10 |
11 | RUN apt-get update && apt-get install libopencv-dev python3-opencv -y
12 |
13 | ADD . /usr/src/app
14 |
15 | CMD python -m slacky
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Max Bridgland
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
5 |
6 | [](https://github.com/M4cs/Slacky/stargazers) [](https://github.com/M4cs/Slacky/network) [](https://github.com/M4cs/Slacky/blob/master/LICENSE) [](https://discord.gg/JjZwJVF) [](https://repl.it/github/M4cs/Slacky)
7 |
8 |
9 | The First Python Selfbot for Slack Workspaces :star:
10 |
11 | ## What is this?
12 |
13 | Slacky is a self-bot for Slack. What is a selfbot? Unlike a normal Slack Bot, Slacky makes all it's request using your personal user token so that it can act as you. Therefore it will edit and post as you rather than a third-party bot making it look like you are doing things extremely fast like custom replies, animations, and more!
14 |
15 | There are a ton of default commands, and easily loadable custom plugins. You are even able to run it in multiple workspaces at once. Read more below for info on the wiki and more commands.
16 |
17 | Discord for Support: https://discord.gg/JjZwJVF
18 |
19 | ### This README doesn't have all the info! I've written a readme with a bunch of info on the bot [here](https://github.com/M4cs/Slacky/wiki). Check it out for information on how to Install, Setup, and Customize Slacky!
20 |
21 | ### Read how to run the bot in multiple workspaces [here](https://github.com/M4cs/Slacky/wiki/Multiple-Workspaces)
22 |
23 | ### [View Changelog Here](https://github.com/M4cs/Slacky/blob/master/CHANGELOG.md)
24 |
25 |
26 |
Slack Client

27 |
28 |
29 |
30 |
Bot Server
31 |
32 |
33 |
34 | ## Default Commands
35 |
36 | Assumes prefix is `~`
37 |
38 | | Command | Description | Usage |
39 | | :--: | :--: | :--: |
40 | | heartbeat | Check if bot is up or not | ~heartbeat |
41 | | ping | Get response time from server hosting bot | ~ping |
42 | | uinfo | Get info about a user | ~uinfo @user |
43 | | winfo | Get info about the current workspace | ~winfo |
44 | | convinfo | Get info about conversation/channel | ~convinfo #chantag|optional |
45 | | info | Get info about the bot | ~info |
46 | | errors | See all errors the bot has encountered | ~errors |
47 | | msgstatus | Enable/Disable random status emoji on msg | ~msgstatus |
48 | | customrs | Manage custom replies to messages | [Read Wiki](https://github.com/M4cs/Slacky/wiki) |
49 | | stats | Get info about the bot running | ~stats |
50 | | ascii | Generate ASCII art from a phrase | ~ascii msg |
51 | | shift | CrEaTe ShIfT tExT lIkE tHiS | ~shift phrase |
52 | | subspace | Replace spaces with emojis | ~subspace :emoji: msg |
53 | | space | Add a space in between each character | ~space phrase |
54 | | ani | Run animation from animation folder | [Read Wiki](https://github.com/M4cs/Slacky/wiki) |
55 | | ud | Get urban dictionary definiton | ~ud query |
56 | | setprefix | Sets bot command prefix | ~setprefix prefix |
57 | | xkcd | Get Daily xkcd comic | ~xkcd |
58 | | delete | Delete X num of your msgs | ~delete num_of_msgs |
59 | | react | React to last sent message | ~react :emoji: |
60 | | reactrand | React to with random emoji | ~reactrand |
61 | | reactspam | Spam 23 Reactions (Notification Spam) | ~randspam |
62 | | howdoi | Find code snippets from stack overflow | ~howdoi loop over list python |
63 | | listener | Add or remove listeners | ~listener add/delete phrase |
64 | | listener list | List all listener words | ~listener list |
65 | | help | Display this message | ~help |
66 |
67 | ## Contributing
68 |
69 | Contributions can be done through Pull Requests or by creating a plugin pack.
70 |
71 | ## License
72 |
73 | I ask that if you fork this do not release as your own. Please credit and mention it's built off this. In fact building a plugin pack rather than a new bot entirely would be best! The license is MIT so I won't stop you but I'm just asking that you respect my wishes :smile:
74 |
--------------------------------------------------------------------------------
/animations/helloworld.txt:
--------------------------------------------------------------------------------
1 | 0.1
2 | h
3 | [f#]
4 | he
5 | [f#]
6 | hel
7 | [f#]
8 | hell
9 | [f#]
10 | hello
11 | [f#]
12 | hello w
13 | [f#]
14 | hello wo
15 | [f#]
16 | hello wor
17 | [f#]
18 | hello worl
19 | [f#]
20 | hello world
21 | [f#]
--------------------------------------------------------------------------------
/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M4cs/Slacky/992dc75297295878ba37fa64a5ff44c50beac890/banner.png
--------------------------------------------------------------------------------
/customrs.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M4cs/Slacky/992dc75297295878ba37fa64a5ff44c50beac890/customrs.gif
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | slacky:
4 | build: .
5 | image: slacky
6 | stdin_open: true
7 | tty: true
8 | volumes:
9 | - ./:/usr/src/slacky
--------------------------------------------------------------------------------
/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M4cs/Slacky/992dc75297295878ba37fa64a5ff44c50beac890/example.gif
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | colorama
2 | pyfiglet
3 | terminaltables
4 | howdoi
5 | httpx
6 | slackclient
7 | nest_asyncio
8 | pillow
9 | opencv-python
10 | requests
11 | pandas
--------------------------------------------------------------------------------
/slacky.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M4cs/Slacky/992dc75297295878ba37fa64a5ff44c50beac890/slacky.gif
--------------------------------------------------------------------------------
/slacky/__init__.py:
--------------------------------------------------------------------------------
1 | from slacky.config import load_config as lc
2 | from slacky.api.auth import authenticate
3 | from colorama import init
4 | from colorama import Fore, Back, Style
5 | from time import time
6 | from argparse import ArgumentParser
7 | import httpx, json, logging, getpass
8 | import nest_asyncio
9 | import datetime
10 |
11 | nest_asyncio.apply()
12 |
13 | class Prefixes:
14 | info = str('[' + Fore.GREEN + Style.BRIGHT + 'INFO' + Style.RESET_ALL + '] ')
15 | warning = str('[' + Fore.YELLOW + Style.BRIGHT + 'WARNING' + Style.RESET_ALL + '] ')
16 | event = str('[' + Fore.BLUE + Style.BRIGHT + 'EVENT' + Style.RESET_ALL + '] ')
17 | error = str('[' + Fore.RED + Style.BRIGHT + 'ERROR' + Style.RESET_ALL + '] ')
18 | start = str('[' + Fore.LIGHTBLUE_EX + Style.BRIGHT + 'SLACKY' + Style.RESET_ALL + '] ')
19 |
20 | class BotMetaData:
21 | def __init__(self):
22 | self.start_time = datetime.datetime.now()
23 | self.command_count = 0
24 | self.error_count = 0
25 | self.warning_count = 0
26 | self.message_count = 0
27 | self.errors = []
28 | self.msgstatus = False
29 | self.needs_update = False
30 |
31 | def error(self, e):
32 | print(Prefixes.error + str(e))
33 | self.errors.append(str(e))
34 | self.error_count += 1
35 |
36 | def get_uptime(self):
37 | now = datetime.datetime.now()
38 | uptime = now - self.start_time
39 | days = uptime.days
40 | hours, remainder = divmod(uptime.seconds, 3600)
41 | minutes, seconds = divmod(remainder, 60)
42 | string = ""
43 | if days == 0:
44 | pass
45 | else:
46 | string += str(str(days) + " days, ")
47 | if hours == 0:
48 | pass
49 | else:
50 | string += str(str(hours) + " hrs, ")
51 | if minutes == 0:
52 | pass
53 | else:
54 | string += str(str(minutes) + " min, ")
55 | if seconds == 0:
56 | pass
57 | else:
58 | string += str(str(seconds) + " sec.")
59 | if string.endswith(', '):
60 | string = string[:-2]
61 | else:
62 | pass
63 | return string
64 |
65 | class CustomReplies:
66 | def __init__(self, config):
67 | self.custom_replies = config['custom_replies']
68 | self.last_sent = False
69 |
70 | def add(self, custom_reply):
71 | self.custom_replies.append(custom_reply)
72 | with open('config.json', 'r+') as file:
73 | obj = json.load(file)
74 | obj['custom_replies'] = self.custom_replies
75 | file.seek(0)
76 | json.dump(obj, file, indent=4)
77 | file.truncate()
78 |
79 | def delete(self, num):
80 | del self.custom_replies[int(num)]
81 | with open('config.json', 'r+') as file:
82 | obj = json.load(file)
83 | obj['custom_replies'] = self.custom_replies
84 | file.seek(0)
85 | json.dump(obj, file, indent=4)
86 | file.truncate()
87 |
88 | class Listeners:
89 | def __init__(self, config):
90 | self.listeners = config['listeners']
91 |
92 | def add(self, phrase):
93 | self.listeners.append(phrase)
94 | with open('config.json', 'r+') as file:
95 | obj = json.load(file)
96 | obj['listeners'] = self.listeners
97 | file.seek(0)
98 | json.dump(obj, file, indent=4)
99 | file.truncate()
100 |
101 | def delete(self, phrase):
102 | num = self.listeners.index(phrase)
103 | del self.listeners[num]
104 | with open('config.json', 'r+') as file:
105 | obj = json.load(file)
106 | obj['listeners'] = self.listeners
107 | file.seek(0)
108 | json.dump(obj, file, indent=4)
109 | file.truncate()
110 |
111 | def check_user(user):
112 | if user == config['user']:
113 | return True
114 | else:
115 | return False
116 |
117 | def config_parser():
118 | parser = ArgumentParser()
119 | parser.add_argument('-c', '--config', help='Optional path to load different config or create new')
120 | return parser
121 |
122 | try:
123 | parser = config_parser()
124 | args = parser.parse_args()
125 | if args.config:
126 | config_path = args.config
127 | else:
128 | config_path = './config.json'
129 | with open('version.txt', 'r') as file:
130 | version = str(file.read())
131 | print(Prefixes.start + 'Welcome to Slacky {} | The First Python Self-Bot for Slack!'.format(version))
132 | print(Prefixes.event + 'Searching for New Updates...')
133 |
134 | remote_v = httpx.get('https://raw.githubusercontent.com/M4cs/Slacky/master/version.txt')
135 | rv = remote_v.content.decode('utf-8')
136 | bot = BotMetaData()
137 | if version != rv:
138 | print(Prefixes.warning + 'Newer Version Available! Please re-pull to update.')
139 | bot.needs_update = True
140 | else:
141 | print(Prefixes.info + 'Up to Date!')
142 | config = lc(config_path)
143 | if not config:
144 | print(Prefixes.warning + 'No Config File Found. Starting Wizard.')
145 | print(Prefixes.start + 'Go to https://slackyauth.maxbridgland.com/authorize and Grab a Token for Slacky 2.0')
146 | token = input('> ')
147 | print(Prefixes.start + 'Enter User ID. Google How To Get This.')
148 | user_id = input('> ')
149 | print(Prefixes.start + 'Enter Desired Prefix (Default: ~)')
150 | prefix = input('> ')
151 | if prefix == '' or prefix == None:
152 | prefix = '~'
153 | else:
154 | prefix = prefix
155 | print(Prefixes.info + 'Entered Token:', token)
156 | print(Prefixes.info + 'Entered User ID:', user_id)
157 | print(Prefixes.info + 'Entered Prefix:', prefix)
158 | print(Prefixes.start + 'Press ENTER to Confirm Information or Ctrl+C to Quit.')
159 | getpass.getpass('')
160 | with open(config_path, 'w+') as file:
161 | config = {
162 | 'token': token,
163 | 'user': user_id,
164 | 'prefix': prefix,
165 | 'listeners': [],
166 | 'custom_replies': []
167 | }
168 | json.dump(config, file, indent=4)
169 | print(Prefixes.event + 'Config Saved! Please Restart To Use Slacky')
170 | exit(0)
171 |
172 | print(Prefixes.info + 'Config Loaded')
173 | print(Prefixes.event + 'Attempting to Authenticate with Slack', end='\r')
174 | listener = Listeners(config)
175 | customrs = CustomReplies(config)
176 | client = authenticate(config)
177 | if not client:
178 | print(Prefixes.error + 'Could Not Authenticate with Slack! Please check your config and token!')
179 | print(' ' * 65, end='\r')
180 | user = client.users_info(user=config['user'])
181 | team = client.team_info()['team']['domain']
182 | print(Prefixes.info + 'Logged in as {}@{}'.format(user['user']['name'], team))
183 | except KeyboardInterrupt:
184 | print(Prefixes.event + 'Shutdown Called')
185 | exit(0)
186 |
--------------------------------------------------------------------------------
/slacky/__main__.py:
--------------------------------------------------------------------------------
1 | from slacky import config, client, Prefixes
2 | from slacky.plugins import *
3 | from time import sleep
4 | import slack
5 | import httpx
6 | import re
7 |
8 | print(Prefixes.event + 'Loading Plugins')
9 |
10 | commands = {
11 | 'heartbeat': lambda **payload: heartbeat,
12 | 'ping': lambda **payload: ping,
13 | 'stats' : lambda **payload: stats,
14 | 'setprefix': lambda **payload: setprefix,
15 | 'space' : lambda **payload: space,
16 | 'winfo': lambda **payload: winfo,
17 | 'uinfo' : lambda **payload: uinfo,
18 | 'convinfo': lambda **payload: convinfo,
19 | 'ani' : lambda **payload: animations,
20 | 'errors': lambda **payload: errors,
21 | 'ud' : lambda **payload: ud,
22 | 'help': lambda **payload: shelp,
23 | 'delete' : lambda **payload: delete,
24 | 'ascii': lambda **payload: ascii,
25 | 'reactrand' : lambda **payload: reactrand,
26 | 'reactspam': lambda **payload: reactspam,
27 | 'customrs' : lambda **payload: customrscmd,
28 | 'howdoi': lambda **payload: howdoicmd,
29 | 'subspace' : lambda **payload: sub_space,
30 | 'xkcd': lambda **payload: xkcd,
31 | 'react' : lambda **payload: react,
32 | 'info': lambda **payload: info,
33 | 'shift' : lambda **payload: shift,
34 | 'status': lambda **payload: status,
35 | 'listener': lambda **payload: listenercmd,
36 | 'msgstatus': lambda **payload: msgstatus,
37 | # Uncomment to load deepfry, requires OpenCV 4
38 | # 'deepfry': lambda **payload: deepfry,
39 | # Uncomment to load shutterstock, requires API credentials,
40 | 'stockpic': lambda **payload: stockpic,
41 | 'coronastatus': lambda **payload: coronavirus
42 | }
43 |
44 | @slack.RTMClient.run_on(event='message')
45 | def _cmdcheck(**payload):
46 | prefix = config['prefix']
47 | text = payload['data'].get('text')
48 | if not text:
49 | return "no text"
50 | cmd = re.search(r'{}(.*?) |{}(.*?)$'.format(prefix, prefix), text)
51 | if not cmd:
52 | return "no command"
53 | cmd = cmd.group(1) if cmd.group(1) else cmd.group(2)
54 | if cmd in commands.keys():
55 | func = commands.get(cmd)()
56 | if func:
57 | return func(**payload)
58 |
59 | @slack.RTMClient.run_on(event='message')
60 | def _customrsd(**payload):
61 | return customrsd(**payload)
62 |
63 | @slack.RTMClient.run_on(event='message')
64 | def _listenerd(**payload):
65 | return listenerd(**payload)
66 |
67 | def run_client(rtm):
68 | rtm.start()
69 |
70 | slack_token = config['token']
71 | rtmclient = slack.RTMClient(token=slack_token)
72 | print(Prefixes.event + 'Default Plugins Loaded')
73 | print(Prefixes.event + 'Custom Plugins Loaded (If Any)')
74 | try:
75 | print(Prefixes.event + 'Running Bot...')
76 | print(Prefixes.start + 'Log Output:')
77 | rtmclient.ping_interval = 2
78 | run_client(rtmclient)
79 | except KeyboardInterrupt:
80 | print(Prefixes.event + 'Shutdown Called')
81 | exit(0)
82 | except Exception as e:
83 | bot.error(e)
84 | bot.error_count += 1
85 | print(Prefixes.event + 'Attempting To Auto Reconnect...')
86 | time.sleep(2)
87 | run_client(rtmclient)
88 |
--------------------------------------------------------------------------------
/slacky/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M4cs/Slacky/992dc75297295878ba37fa64a5ff44c50beac890/slacky/api/__init__.py
--------------------------------------------------------------------------------
/slacky/api/auth.py:
--------------------------------------------------------------------------------
1 | import slack
2 |
3 | def authenticate(config):
4 | try:
5 | client = slack.WebClient(token=config['token'])
6 | return client
7 | except:
8 | return None
--------------------------------------------------------------------------------
/slacky/config/__init__.py:
--------------------------------------------------------------------------------
1 | from .load import load_config
--------------------------------------------------------------------------------
/slacky/config/load.py:
--------------------------------------------------------------------------------
1 | import json, os
2 |
3 | def load_config(cp):
4 | if not os.path.exists(cp):
5 | return None
6 | with open(cp, 'rb') as config_file:
7 | config = json.load(config_file)
8 | return config
--------------------------------------------------------------------------------
/slacky/constants/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M4cs/Slacky/992dc75297295878ba37fa64a5ff44c50beac890/slacky/constants/__init__.py
--------------------------------------------------------------------------------
/slacky/constants/emojis.py:
--------------------------------------------------------------------------------
1 | emojis = ['hash', 'keycap_star', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'copyright', 'registered', 'mahjong', 'black_joker', 'a', 'b', 'o2', 'parking', 'ab', 'cl', 'cool', 'free', 'id', 'new', 'ng', 'ok', 'sos', 'up', 'vs', 'flag-ac', 'flag-ad', 'flag-ae', 'flag-af', 'flag-ag', 'flag-ai', 'flag-al', 'flag-am', 'flag-ao', 'flag-aq', 'flag-ar', 'flag-as', 'flag-at', 'flag-au', 'flag-aw', 'flag-ax', 'flag-az', 'flag-ba', 'flag-bb', 'flag-bd', 'flag-be', 'flag-bf', 'flag-bg', 'flag-bh', 'flag-bi', 'flag-bj', 'flag-bl', 'flag-bm', 'flag-bn', 'flag-bo', 'flag-bq', 'flag-br', 'flag-bs', 'flag-bt', 'flag-bv', 'flag-bw', 'flag-by', 'flag-bz', 'flag-ca', 'flag-cc', 'flag-cd', 'flag-cf', 'flag-cg', 'flag-ch', 'flag-ci', 'flag-ck', 'flag-cl', 'flag-cm', 'cn', 'flag-co', 'flag-cp', 'flag-cr', 'flag-cu', 'flag-cv', 'flag-cw', 'flag-cx', 'flag-cy', 'flag-cz', 'de', 'flag-dg', 'flag-dj', 'flag-dk', 'flag-dm', 'flag-do', 'flag-dz', 'flag-ea', 'flag-ec', 'flag-ee', 'flag-eg', 'flag-eh', 'flag-er', 'es', 'flag-et', 'flag-eu', 'flag-fi', 'flag-fj', 'flag-fk', 'flag-fm', 'flag-fo', 'fr', 'flag-ga', 'gb', 'flag-gd', 'flag-ge', 'flag-gf', 'flag-gg', 'flag-gh', 'flag-gi', 'flag-gl', 'flag-gm', 'flag-gn', 'flag-gp', 'flag-gq', 'flag-gr', 'flag-gs', 'flag-gt', 'flag-gu', 'flag-gw', 'flag-gy', 'flag-hk', 'flag-hm', 'flag-hn', 'flag-hr', 'flag-ht', 'flag-hu', 'flag-ic', 'flag-id', 'flag-ie', 'flag-il', 'flag-im', 'flag-in', 'flag-io', 'flag-iq', 'flag-ir', 'flag-is', 'it', 'flag-je', 'flag-jm', 'flag-jo', 'jp', 'flag-ke', 'flag-kg', 'flag-kh', 'flag-ki', 'flag-km', 'flag-kn', 'flag-kp', 'kr', 'flag-kw', 'flag-ky', 'flag-kz', 'flag-la', 'flag-lb', 'flag-lc', 'flag-li', 'flag-lk', 'flag-lr', 'flag-ls', 'flag-lt', 'flag-lu', 'flag-lv', 'flag-ly', 'flag-ma', 'flag-mc', 'flag-md', 'flag-me', 'flag-mf', 'flag-mg', 'flag-mh', 'flag-mk', 'flag-ml', 'flag-mm', 'flag-mn', 'flag-mo', 'flag-mp', 'flag-mq', 'flag-mr', 'flag-ms', 'flag-mt', 'flag-mu', 'flag-mv', 'flag-mw', 'flag-mx', 'flag-my', 'flag-mz', 'flag-na', 'flag-nc', 'flag-ne', 'flag-nf', 'flag-ng', 'flag-ni', 'flag-nl', 'flag-no', 'flag-np', 'flag-nr', 'flag-nu', 'flag-nz', 'flag-om', 'flag-pa', 'flag-pe', 'flag-pf', 'flag-pg', 'flag-ph', 'flag-pk', 'flag-pl', 'flag-pm', 'flag-pn', 'flag-pr', 'flag-ps', 'flag-pt', 'flag-pw', 'flag-py', 'flag-qa', 'flag-re', 'flag-ro', 'flag-rs', 'ru', 'flag-rw', 'flag-sa', 'flag-sb', 'flag-sc', 'flag-sd', 'flag-se', 'flag-sg', 'flag-sh', 'flag-si', 'flag-sj', 'flag-sk', 'flag-sl', 'flag-sm', 'flag-sn', 'flag-so', 'flag-sr', 'flag-ss', 'flag-st', 'flag-sv', 'flag-sx', 'flag-sy', 'flag-sz', 'flag-ta', 'flag-tc', 'flag-td', 'flag-tf', 'flag-tg', 'flag-th', 'flag-tj', 'flag-tk', 'flag-tl', 'flag-tm', 'flag-tn', 'flag-to', 'flag-tr', 'flag-tt', 'flag-tv', 'flag-tw', 'flag-tz', 'flag-ua', 'flag-ug', 'flag-um', 'flag-un', 'us', 'flag-uy', 'flag-uz', 'flag-va', 'flag-vc', 'flag-ve', 'flag-vg', 'flag-vi', 'flag-vn', 'flag-vu', 'flag-wf', 'flag-ws', 'flag-xk', 'flag-ye', 'flag-yt', 'flag-za', 'flag-zm', 'flag-zw', 'koko', 'sa', 'u7121', 'u6307', 'u7981', 'u7a7a', 'u5408', 'u6e80', 'u6709', 'u6708', 'u7533', 'u5272', 'u55b6', 'ideograph_advantage', 'accept', 'cyclone', 'foggy', 'closed_umbrella', 'night_with_stars', 'sunrise_over_mountains', 'sunrise', 'city_sunset', 'city_sunrise', 'rainbow', 'bridge_at_night', 'ocean', 'volcano', 'milky_way', 'earth_africa', 'earth_americas', 'earth_asia', 'globe_with_meridians', 'new_moon', 'waxing_crescent_moon', 'first_quarter_moon', 'moon', 'full_moon', 'waning_gibbous_moon', 'last_quarter_moon', 'waning_crescent_moon', 'crescent_moon', 'new_moon_with_face', 'first_quarter_moon_with_face', 'last_quarter_moon_with_face', 'full_moon_with_face', 'sun_with_face', 'star2', 'stars', 'thermometer', 'mostly_sunny', 'barely_sunny', 'partly_sunny_rain', 'rain_cloud', 'snow_cloud', 'lightning', 'tornado', 'fog', 'wind_blowing_face', 'hotdog', 'taco', 'burrito', 'chestnut', 'seedling', 'evergreen_tree', 'deciduous_tree', 'palm_tree', 'cactus', 'hot_pepper', 'tulip', 'cherry_blossom', 'rose', 'hibiscus', 'sunflower', 'blossom', 'corn', 'ear_of_rice', 'herb', 'four_leaf_clover', 'maple_leaf', 'fallen_leaf', 'leaves', 'mushroom', 'tomato', 'eggplant', 'grapes', 'melon', 'watermelon', 'tangerine', 'lemon', 'banana', 'pineapple', 'apple', 'green_apple', 'pear', 'peach', 'cherries', 'strawberry', 'hamburger', 'pizza', 'meat_on_bone', 'poultry_leg', 'rice_cracker', 'rice_ball', 'rice', 'curry', 'ramen', 'spaghetti', 'bread', 'fries', 'sweet_potato', 'dango', 'oden', 'sushi', 'fried_shrimp', 'fish_cake', 'icecream', 'shaved_ice', 'ice_cream', 'doughnut', 'cookie', 'chocolate_bar', 'candy', 'lollipop', 'custard', 'honey_pot', 'cake', 'bento', 'stew', 'fried_egg', 'fork_and_knife', 'tea', 'sake', 'wine_glass', 'cocktail', 'tropical_drink', 'beer', 'beers', 'baby_bottle', 'knife_fork_plate', 'champagne', 'popcorn', 'ribbon', 'gift', 'birthday', 'jack_o_lantern', 'christmas_tree', 'santa', 'fireworks', 'sparkler', 'balloon', 'tada', 'confetti_ball', 'tanabata_tree', 'crossed_flags', 'bamboo', 'dolls', 'flags', 'wind_chime', 'rice_scene', 'school_satchel', 'mortar_board', 'medal', 'reminder_ribbon', 'studio_microphone', 'level_slider', 'control_knobs', 'film_frames', 'admission_tickets', 'carousel_horse', 'ferris_wheel', 'roller_coaster', 'fishing_pole_and_fish', 'microphone', 'movie_camera', 'cinema', 'headphones', 'art', 'tophat', 'circus_tent', 'ticket', 'clapper', 'performing_arts', 'video_game', 'dart', 'slot_machine', '8ball', 'game_die', 'bowling', 'flower_playing_cards', 'musical_note', 'notes', 'saxophone', 'guitar', 'musical_keyboard', 'trumpet', 'violin', 'musical_score', 'running_shirt_with_sash', 'tennis', 'ski', 'basketball', 'checkered_flag', 'snowboarder', 'woman-running', 'man-running', 'runner', 'woman-surfing', 'man-surfing', 'surfer', 'sports_medal', 'trophy', 'horse_racing', 'football', 'rugby_football', 'woman-swimming', 'man-swimming', 'swimmer', 'woman-lifting-weights', 'man-lifting-weights', 'weight_lifter', 'woman-golfing', 'man-golfing', 'golfer', 'racing_motorcycle', 'racing_car', 'cricket_bat_and_ball', 'volleyball', 'field_hockey_stick_and_ball', 'ice_hockey_stick_and_puck', 'table_tennis_paddle_and_ball', 'snow_capped_mountain', 'camping', 'beach_with_umbrella', 'building_construction', 'house_buildings', 'cityscape', 'derelict_house_building', 'classical_building', 'desert', 'desert_island', 'national_park', 'stadium', 'house', 'house_with_garden', 'office', 'post_office', 'european_post_office', 'hospital', 'bank', 'atm', 'hotel', 'love_hotel', 'convenience_store', 'school', 'department_store', 'factory', 'izakaya_lantern', 'japanese_castle', 'european_castle', 'rainbow-flag', 'waving_white_flag', 'pirate_flag', 'flag-england', 'flag-scotland', 'flag-wales', 'waving_black_flag', 'rosette', 'label', 'badminton_racquet_and_shuttlecock', 'bow_and_arrow', 'amphora', 'skin-tone-2', 'skin-tone-3', 'skin-tone-4', 'skin-tone-5', 'skin-tone-6', 'rat', 'mouse2', 'ox', 'water_buffalo', 'cow2', 'tiger2', 'leopard', 'rabbit2', 'cat2', 'dragon', 'crocodile', 'whale2', 'snail', 'snake', 'racehorse', 'ram', 'goat', 'sheep', 'monkey', 'rooster', 'chicken', 'dog2', 'pig2', 'boar', 'elephant', 'octopus', 'shell', 'bug', 'ant', 'bee', 'beetle', 'fish', 'tropical_fish', 'blowfish', 'turtle', 'hatching_chick', 'baby_chick', 'hatched_chick', 'bird', 'penguin', 'koala', 'poodle', 'dromedary_camel', 'camel', 'dolphin', 'mouse', 'cow', 'tiger', 'rabbit', 'cat', 'dragon_face', 'whale', 'horse', 'monkey_face', 'dog', 'pig', 'frog', 'hamster', 'wolf', 'bear', 'panda_face', 'pig_nose', 'feet', 'chipmunk', 'eyes', 'eye-in-speech-bubble', 'eye', 'ear', 'nose', 'lips', 'tongue', 'point_up_2', 'point_down', 'point_left', 'point_right', 'facepunch', 'wave', 'ok_hand', '+1', '-1', 'clap', 'open_hands', 'crown', 'womans_hat', 'eyeglasses', 'necktie', 'shirt', 'jeans', 'dress', 'kimono', 'bikini', 'womans_clothes', 'purse', 'handbag', 'pouch', 'mans_shoe', 'athletic_shoe', 'high_heel', 'sandal', 'boot', 'footprints', 'bust_in_silhouette', 'busts_in_silhouette', 'boy', 'girl', 'male-farmer', 'male-cook', 'male-student', 'male-singer', 'male-artist', 'male-teacher', 'male-factory-worker', 'man-boy-boy', 'man-boy', 'man-girl-boy', 'man-girl-girl', 'man-girl', 'man-man-boy', 'man-man-boy-boy', 'man-man-girl', 'man-man-girl-boy', 'man-man-girl-girl', 'man-woman-boy', 'man-woman-boy-boy', 'man-woman-girl', 'man-woman-girl-boy', 'man-woman-girl-girl', 'male-technologist', 'male-office-worker', 'male-mechanic', 'male-scientist', 'male-astronaut', 'male-firefighter', 'male_red_haired', 'male_curly_haired', 'male_bald', 'male_white_haired', 'male-doctor', 'male-judge', 'male-pilot', 'man-heart-man', 'man-kiss-man', 'man', 'female-farmer', 'female-cook', 'female-student', 'female-singer', 'female-artist', 'female-teacher', 'female-factory-worker', 'woman-boy-boy', 'woman-boy', 'woman-girl-boy', 'woman-girl-girl', 'woman-girl', 'woman-woman-boy', 'woman-woman-boy-boy', 'woman-woman-girl', 'woman-woman-girl-boy', 'woman-woman-girl-girl', 'female-technologist', 'female-office-worker', 'female-mechanic', 'female-scientist', 'female-astronaut', 'female-firefighter', 'female_red_haired', 'female_curly_haired', 'female_bald', 'female_white_haired', 'female-doctor', 'female-judge', 'female-pilot', 'woman-heart-man', 'woman-heart-woman', 'woman-kiss-man', 'woman-kiss-woman', 'woman', 'family', 'couple', 'two_men_holding_hands', 'two_women_holding_hands', 'female-police-officer', 'male-police-officer', 'cop', 'woman-with-bunny-ears-partying', 'man-with-bunny-ears-partying', 'dancers', 'bride_with_veil', 'blond-haired-woman', 'blond-haired-man', 'person_with_blond_hair', 'man_with_gua_pi_mao', 'woman-wearing-turban', 'man-wearing-turban', 'man_with_turban', 'older_man', 'older_woman', 'baby', 'female-construction-worker', 'male-construction-worker', 'construction_worker', 'princess', 'japanese_ogre', 'japanese_goblin', 'ghost', 'angel', 'alien', 'space_invader', 'imp', 'skull', 'woman-tipping-hand', 'man-tipping-hand', 'information_desk_person', 'female-guard', 'male-guard', 'guardsman', 'dancer', 'lipstick', 'nail_care', 'woman-getting-massage', 'man-getting-massage', 'massage', 'woman-getting-haircut', 'man-getting-haircut', 'haircut', 'barber', 'syringe', 'pill', 'kiss', 'love_letter', 'ring', 'gem', 'couplekiss', 'bouquet', 'couple_with_heart', 'wedding', 'heartbeat', 'broken_heart', 'two_hearts', 'sparkling_heart', 'heartpulse', 'cupid', 'blue_heart', 'green_heart', 'yellow_heart', 'purple_heart', 'gift_heart', 'revolving_hearts', 'heart_decoration', 'diamond_shape_with_a_dot_inside', 'bulb', 'anger', 'bomb', 'zzz', 'boom', 'sweat_drops', 'droplet', 'dash', 'hankey', 'muscle', 'dizzy', 'speech_balloon', 'thought_balloon', 'white_flower', '100', 'moneybag', 'currency_exchange', 'heavy_dollar_sign', 'credit_card', 'yen', 'dollar', 'euro', 'pound', 'money_with_wings', 'chart', 'seat', 'computer', 'briefcase', 'minidisc', 'floppy_disk', 'cd', 'dvd', 'file_folder', 'open_file_folder', 'page_with_curl', 'page_facing_up', 'date', 'calendar', 'card_index', 'chart_with_upwards_trend', 'chart_with_downwards_trend', 'bar_chart', 'clipboard', 'pushpin', 'round_pushpin', 'paperclip', 'straight_ruler', 'triangular_ruler', 'bookmark_tabs', 'ledger', 'notebook', 'notebook_with_decorative_cover', 'closed_book', 'book', 'green_book', 'blue_book', 'orange_book', 'books', 'name_badge', 'scroll', 'memo', 'telephone_receiver', 'pager', 'fax', 'satellite_antenna', 'loudspeaker', 'mega', 'outbox_tray', 'inbox_tray', 'package', 'e-mail', 'incoming_envelope', 'envelope_with_arrow', 'mailbox_closed', 'mailbox', 'mailbox_with_mail', 'mailbox_with_no_mail', 'postbox', 'postal_horn', 'newspaper', 'iphone', 'calling', 'vibration_mode', 'mobile_phone_off', 'no_mobile_phones', 'signal_strength', 'camera', 'camera_with_flash', 'video_camera', 'tv', 'radio', 'vhs', 'film_projector', 'prayer_beads', 'twisted_rightwards_arrows', 'repeat', 'repeat_one', 'arrows_clockwise', 'arrows_counterclockwise', 'low_brightness', 'high_brightness', 'mute', 'speaker', 'sound', 'loud_sound', 'battery', 'electric_plug', 'mag', 'mag_right', 'lock_with_ink_pen', 'closed_lock_with_key', 'key', 'lock', 'unlock', 'bell', 'no_bell', 'bookmark', 'link', 'radio_button', 'back', 'end', 'on', 'soon', 'top', 'underage', 'keycap_ten', 'capital_abcd', 'abcd', '1234', 'symbols', 'abc', 'fire', 'flashlight', 'wrench', 'hammer', 'nut_and_bolt', 'hocho', 'gun', 'microscope', 'telescope', 'crystal_ball', 'six_pointed_star', 'beginner', 'trident', 'black_square_button', 'white_square_button', 'red_circle', 'large_blue_circle', 'large_orange_diamond', 'large_blue_diamond', 'small_orange_diamond', 'small_blue_diamond', 'small_red_triangle', 'small_red_triangle_down', 'arrow_up_small', 'arrow_down_small', 'om_symbol', 'dove_of_peace', 'kaaba', 'mosque', 'synagogue', 'menorah_with_nine_branches', 'clock1', 'clock2', 'clock3', 'clock4', 'clock5', 'clock6', 'clock7', 'clock8', 'clock9', 'clock10', 'clock11', 'clock12', 'clock130', 'clock230', 'clock330', 'clock430', 'clock530', 'clock630', 'clock730', 'clock830', 'clock930', 'clock1030', 'clock1130', 'clock1230', 'candle', 'mantelpiece_clock', 'hole', 'man_in_business_suit_levitating', 'female-detective', 'male-detective', 'sleuth_or_spy', 'dark_sunglasses', 'spider', 'spider_web', 'joystick', 'man_dancing', 'linked_paperclips', 'lower_left_ballpoint_pen', 'lower_left_fountain_pen', 'lower_left_paintbrush', 'lower_left_crayon', 'raised_hand_with_fingers_splayed', 'middle_finger', 'spock-hand', 'black_heart', 'desktop_computer', 'printer', 'three_button_mouse', 'trackball', 'frame_with_picture', 'card_index_dividers', 'card_file_box', 'file_cabinet', 'wastebasket', 'spiral_note_pad', 'spiral_calendar_pad', 'compression', 'old_key', 'rolled_up_newspaper', 'dagger_knife', 'speaking_head_in_silhouette', 'left_speech_bubble', 'right_anger_bubble', 'ballot_box_with_ballot', 'world_map', 'mount_fuji', 'tokyo_tower', 'statue_of_liberty', 'japan', 'moyai', 'grinning', 'grin', 'joy', 'smiley', 'smile', 'sweat_smile', 'laughing', 'innocent', 'smiling_imp', 'wink', 'blush', 'yum', 'relieved', 'heart_eyes', 'sunglasses', 'smirk', 'neutral_face', 'expressionless', 'unamused', 'sweat', 'pensive', 'confused', 'confounded', 'kissing', 'kissing_heart', 'kissing_smiling_eyes', 'kissing_closed_eyes', 'stuck_out_tongue', 'stuck_out_tongue_winking_eye', 'stuck_out_tongue_closed_eyes', 'disappointed', 'worried', 'angry', 'rage', 'cry', 'persevere', 'triumph', 'disappointed_relieved', 'frowning', 'anguished', 'fearful', 'weary', 'sleepy', 'tired_face', 'grimacing', 'sob', 'open_mouth', 'hushed', 'cold_sweat', 'scream', 'astonished', 'flushed', 'sleeping', 'dizzy_face', 'no_mouth', 'mask', 'smile_cat', 'joy_cat', 'smiley_cat', 'heart_eyes_cat', 'smirk_cat', 'kissing_cat', 'pouting_cat', 'crying_cat_face', 'scream_cat', 'slightly_frowning_face', 'slightly_smiling_face', 'upside_down_face', 'face_with_rolling_eyes', 'woman-gesturing-no', 'man-gesturing-no', 'no_good', 'woman-gesturing-ok', 'man-gesturing-ok', 'ok_woman', 'woman-bowing', 'man-bowing', 'bow', 'see_no_evil', 'hear_no_evil', 'speak_no_evil', 'woman-raising-hand', 'man-raising-hand', 'raising_hand', 'raised_hands', 'woman-frowning', 'man-frowning', 'person_frowning', 'woman-pouting', 'man-pouting', 'person_with_pouting_face', 'pray', 'rocket', 'helicopter', 'steam_locomotive', 'railway_car', 'bullettrain_side', 'bullettrain_front', 'train2', 'metro', 'light_rail', 'station', 'tram', 'train', 'bus', 'oncoming_bus', 'trolleybus', 'busstop', 'minibus', 'ambulance', 'fire_engine', 'police_car', 'oncoming_police_car', 'taxi', 'oncoming_taxi', 'car', 'oncoming_automobile', 'blue_car', 'truck', 'articulated_lorry', 'tractor', 'monorail', 'mountain_railway', 'suspension_railway', 'mountain_cableway', 'aerial_tramway', 'ship', 'woman-rowing-boat', 'man-rowing-boat', 'rowboat', 'speedboat', 'traffic_light', 'vertical_traffic_light', 'construction', 'rotating_light', 'triangular_flag_on_post', 'door', 'no_entry_sign', 'smoking', 'no_smoking', 'put_litter_in_its_place', 'do_not_litter', 'potable_water', 'non-potable_water', 'bike', 'no_bicycles', 'woman-biking', 'man-biking', 'bicyclist', 'woman-mountain-biking', 'man-mountain-biking', 'mountain_bicyclist', 'woman-walking', 'man-walking', 'walking', 'no_pedestrians', 'children_crossing', 'mens', 'womens', 'restroom', 'baby_symbol', 'toilet', 'wc', 'shower', 'bath', 'bathtub', 'passport_control', 'customs', 'baggage_claim', 'left_luggage', 'couch_and_lamp', 'sleeping_accommodation', 'shopping_bags', 'bellhop_bell', 'bed', 'place_of_worship', 'octagonal_sign', 'shopping_trolley', 'hammer_and_wrench', 'shield', 'oil_drum', 'motorway', 'railway_track', 'motor_boat', 'small_airplane', 'airplane_departure', 'airplane_arriving', 'satellite', 'passenger_ship', 'scooter', 'motor_scooter', 'canoe', 'sled', 'flying_saucer', 'skateboard', 'zipper_mouth_face', 'money_mouth_face', 'face_with_thermometer', 'nerd_face', 'thinking_face', 'face_with_head_bandage', 'robot_face', 'hugging_face', 'the_horns', 'call_me_hand', 'raised_back_of_hand', 'left-facing_fist', 'right-facing_fist', 'handshake', 'crossed_fingers', 'i_love_you_hand_sign', 'face_with_cowboy_hat', 'clown_face', 'nauseated_face', 'rolling_on_the_floor_laughing', 'drooling_face', 'lying_face', 'woman-facepalming', 'man-facepalming', 'face_palm', 'sneezing_face', 'face_with_raised_eyebrow', 'star-struck', 'zany_face', 'shushing_face', 'face_with_symbols_on_mouth', 'face_with_hand_over_mouth', 'face_vomiting', 'exploding_head', 'pregnant_woman', 'breast-feeding', 'palms_up_together', 'selfie', 'prince', 'man_in_tuxedo', 'mrs_claus', 'woman-shrugging', 'man-shrugging', 'shrug', 'woman-cartwheeling', 'man-cartwheeling', 'person_doing_cartwheel', 'woman-juggling', 'man-juggling', 'juggling', 'fencer', 'woman-wrestling', 'man-wrestling', 'wrestlers', 'woman-playing-water-polo', 'man-playing-water-polo', 'water_polo', 'woman-playing-handball', 'man-playing-handball', 'handball', 'wilted_flower', 'drum_with_drumsticks', 'clinking_glasses', 'tumbler_glass', 'spoon', 'goal_net', 'first_place_medal', 'second_place_medal', 'third_place_medal', 'boxing_glove', 'martial_arts_uniform', 'curling_stone', 'lacrosse', 'softball', 'flying_disc', 'croissant', 'avocado', 'cucumber', 'bacon', 'potato', 'carrot', 'baguette_bread', 'green_salad', 'shallow_pan_of_food', 'stuffed_flatbread', 'egg', 'glass_of_milk', 'peanuts', 'kiwifruit', 'pancakes', 'dumpling', 'fortune_cookie', 'takeout_box', 'chopsticks', 'bowl_with_spoon', 'cup_with_straw', 'coconut', 'broccoli', 'pie', 'pretzel', 'cut_of_meat', 'sandwich', 'canned_food', 'leafy_green', 'mango', 'moon_cake', 'bagel', 'smiling_face_with_3_hearts', 'partying_face', 'woozy_face', 'hot_face', 'cold_face', 'pleading_face', 'lab_coat', 'goggles', 'hiking_boot', 'womans_flat_shoe', 'crab', 'lion_face', 'scorpion', 'turkey', 'unicorn_face', 'eagle', 'duck', 'bat', 'shark', 'owl', 'fox_face', 'butterfly', 'deer', 'gorilla', 'lizard', 'rhinoceros', 'shrimp', 'squid', 'giraffe_face', 'zebra_face', 'hedgehog', 'sauropod', 't-rex', 'cricket', 'kangaroo', 'llama', 'peacock', 'hippopotamus', 'parrot', 'raccoon', 'lobster', 'mosquito', 'microbe', 'badger', 'swan', 'bone', 'leg', 'foot', 'tooth', 'female_superhero', 'male_superhero', 'female_supervillain', 'male_supervillain', 'cheese_wedge', 'cupcake', 'salt', 'face_with_monocle', 'adult', 'child', 'older_adult', 'bearded_person', 'person_with_headscarf', 'woman_in_steamy_room', 'man_in_steamy_room', 'person_in_steamy_room', 'woman_climbing', 'man_climbing', 'person_climbing', 'woman_in_lotus_position', 'man_in_lotus_position', 'person_in_lotus_position', 'female_mage', 'male_mage', 'mage', 'female_fairy', 'male_fairy', 'fairy', 'female_vampire', 'male_vampire', 'vampire', 'mermaid', 'merman', 'merperson', 'female_elf', 'male_elf', 'elf', 'female_genie', 'male_genie', 'genie', 'female_zombie', 'male_zombie', 'zombie', 'brain', 'orange_heart', 'billed_cap', 'scarf', 'gloves', 'coat', 'socks', 'red_envelope', 'firecracker', 'jigsaw', 'test_tube', 'petri_dish', 'dna', 'compass', 'abacus', 'fire_extinguisher', 'toolbox', 'bricks', 'magnet', 'luggage', 'lotion_bottle', 'thread', 'yarn', 'safety_pin', 'teddy_bear', 'broom', 'basket', 'roll_of_paper', 'soap', 'sponge', 'receipt', 'nazar_amulet', 'bangbang', 'interrobang', 'tm', 'information_source', 'left_right_arrow', 'arrow_up_down', 'arrow_upper_left', 'arrow_upper_right', 'arrow_lower_right', 'arrow_lower_left', 'leftwards_arrow_with_hook', 'arrow_right_hook', 'watch', 'hourglass', 'keyboard', 'eject', 'fast_forward', 'rewind', 'arrow_double_up', 'arrow_double_down', 'black_right_pointing_double_triangle_with_vertical_bar', 'black_left_pointing_double_triangle_with_vertical_bar', 'black_right_pointing_triangle_with_double_vertical_bar', 'alarm_clock', 'stopwatch', 'timer_clock', 'hourglass_flowing_sand', 'double_vertical_bar', 'black_square_for_stop', 'black_circle_for_record', 'm', 'black_small_square', 'white_small_square', 'arrow_forward', 'arrow_backward', 'white_medium_square', 'black_medium_square', 'white_medium_small_square', 'black_medium_small_square', 'sunny', 'cloud', 'umbrella', 'snowman', 'comet', 'phone', 'ballot_box_with_check', 'umbrella_with_rain_drops', 'coffee', 'shamrock', 'point_up', 'skull_and_crossbones', 'radioactive_sign', 'biohazard_sign', 'orthodox_cross', 'star_and_crescent', 'peace_symbol', 'yin_yang', 'wheel_of_dharma', 'white_frowning_face', 'relaxed', 'female_sign', 'male_sign', 'aries', 'taurus', 'gemini', 'cancer', 'leo', 'virgo', 'libra', 'scorpius', 'sagittarius', 'capricorn', 'aquarius', 'pisces', 'chess_pawn', 'spades', 'clubs', 'hearts', 'diamonds', 'hotsprings', 'recycle', 'infinity', 'wheelchair', 'hammer_and_pick', 'anchor', 'crossed_swords', 'medical_symbol', 'scales', 'alembic', 'gear', 'atom_symbol', 'fleur_de_lis', 'warning', 'zap', 'white_circle', 'black_circle', 'coffin', 'funeral_urn', 'soccer', 'baseball', 'snowman_without_snow', 'partly_sunny', 'thunder_cloud_and_rain', 'ophiuchus', 'pick', 'helmet_with_white_cross', 'chains', 'no_entry', 'shinto_shrine', 'church', 'mountain', 'umbrella_on_ground', 'fountain', 'golf', 'ferry', 'boat', 'skier', 'ice_skate', 'woman-bouncing-ball', 'man-bouncing-ball', 'person_with_ball', 'tent', 'fuelpump', 'scissors', 'white_check_mark', 'airplane', 'email', 'fist', 'hand', 'v', 'writing_hand', 'pencil2', 'black_nib', 'heavy_check_mark', 'heavy_multiplication_x', 'latin_cross', 'star_of_david', 'sparkles', 'eight_spoked_asterisk', 'eight_pointed_black_star', 'snowflake', 'sparkle', 'x', 'negative_squared_cross_mark', 'question', 'grey_question', 'grey_exclamation', 'exclamation', 'heavy_heart_exclamation_mark_ornament', 'heart', 'heavy_plus_sign', 'heavy_minus_sign', 'heavy_division_sign', 'arrow_right', 'curly_loop', 'loop', 'arrow_heading_up', 'arrow_heading_down', 'arrow_left', 'arrow_up', 'arrow_down', 'black_large_square', 'white_large_square', 'star', 'o', 'wavy_dash', 'part_alternation_mark', 'congratulations', 'secret']
--------------------------------------------------------------------------------
/slacky/helpers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M4cs/Slacky/992dc75297295878ba37fa64a5ff44c50beac890/slacky/helpers/__init__.py
--------------------------------------------------------------------------------
/slacky/plugins/__init__.py:
--------------------------------------------------------------------------------
1 | from slacky import config, client, Prefixes, listener, check_user, customrs, bot, version
2 | from slacky.constants.emojis import emojis
3 | from slack.errors import SlackApiError
4 | from terminaltables import DoubleTable
5 | from howdoi import howdoi
6 | from threading import Thread
7 | from pyfiglet import Figlet
8 | from io import BytesIO
9 | import requests
10 | import pandas as pd
11 | import json
12 | import slack
13 | import httpx
14 | import time
15 | import random
16 | import re
17 | import glob
18 | import os
19 | import ntpath
20 |
21 | def cmd_setup(command, **payload):
22 | data = payload['data']
23 | channel_id = data['channel']
24 | user = data.get('user')
25 | timestamp = data.get('ts')
26 | web_client = client
27 | text = data.get('text')
28 | if text and check_user(user):
29 | text_split = text.split(' ')
30 | cmd = text_split[0]
31 | if cmd == config['prefix'] + command:
32 | print(Prefixes.event + 'Ran Command: {}'.format(command))
33 | bot.command_count += 1
34 | return data, channel_id, user, timestamp, web_client, text, text_split
35 | else:
36 | return None, None, None, None, None, None, None
37 | else:
38 | return None, None, None, None, None, None, None
39 |
40 | from slacky.plugins.custom import *
41 |
42 | def coronavirus(**payload):
43 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('coronastatus', **payload)
44 | if data:
45 | try:
46 | web_client.chat_update(
47 | channel=channel_id,
48 | ts=timestamp,
49 | text="One Second"
50 | )
51 | except SlackApiError as e:
52 | bot.error(e)
53 | csv_content = requests.get('https://docs.google.com/spreadsheets/d/1yZv9w9zRKwrGTaR-YzmAqMefw4wMlaXocejdxZaTs6w/export?usp=sharing&format=csv').content
54 | df = pd.read_csv(BytesIO(csv_content), index_col=0)
55 | places = df['Country/Region']
56 | confirmed = df['Confirmed']
57 | deaths = df['Deaths']
58 | recovered = df['Recovered']
59 | total_confirm = confirmed.sum()
60 | total_deaths = deaths.sum()
61 | total_recov = recovered.sum()
62 | region_dict = {}
63 | for place, confirm, death in zip(places, confirmed, deaths):
64 | if region_dict.get(place):
65 | region_dict[place]['confirms'] = int(region_dict[place].get('confirms')) + int(confirm) if str(confirm).lower() != 'nan' else 0
66 | region_dict[place]['deaths'] = int(region_dict[place].get('deaths')) + int(death) if str(death).lower() != 'nan' else 0
67 | else:
68 | region_dict[place] = {
69 | 'confirms': int(confirm) if str(confirm).lower() != 'nan' else 0,
70 | 'deaths': int(death) if str(death).lower() != 'nan' else 0,
71 | }
72 | blocks = [
73 | {
74 | 'type': 'section',
75 | 'text': {
76 | 'type': 'mrkdwn',
77 | 'text': '*Status of Coronavirus:*'
78 | }
79 | },
80 | {
81 | 'type': 'section',
82 | 'fields': [
83 | {
84 | 'type': 'mrkdwn',
85 | 'text': ':mask: *Confirmed Cases: {}*'.format(int(total_confirm))
86 | },
87 | {
88 | 'type': 'mrkdwn',
89 | 'text': ':skull: *Deaths: {}*'.format(int(total_deaths))
90 | },
91 | {
92 | 'type': 'mrkdwn',
93 | 'text': ':muscle: *Recovered: {}*'.format(int(total_recov))
94 | }
95 | ]
96 | }
97 | ]
98 | blocks.append({"type": "section", "fields": []})
99 | count = 0
100 | for k, v in region_dict.items():
101 | if count == 10:
102 | break
103 | blocks[2]['fields'].append({
104 | "type": "mrkdwn",
105 | "text": "*Region:* {}\n*Confirmed:* {}\n*Deaths:* {}\n==================\n".format(k, region_dict[k]['confirms'], region_dict[k]['deaths'])
106 | })
107 | count += 1
108 | blocks.append({"type": "section", "fields": []})
109 | new_count = 0
110 | for k, v in region_dict.items():
111 | if new_count >= count:
112 | blocks[3]['fields'].append({
113 | "type": "mrkdwn",
114 | "text": "*Region:* {}\n*Confirmed:* {}\n*Deaths:* {}\n=================\n".format(k, region_dict[k]['confirms'], region_dict[k]['deaths'])
115 | })
116 | new_count += 1
117 |
118 | try:
119 | web_client.chat_update(
120 | channel=channel_id,
121 | ts=timestamp,
122 | attachments=[{
123 | 'color': '#f43d2d',
124 | 'blocks': blocks
125 | }]
126 | )
127 | except SlackApiError as e:
128 | bot.error(e)
129 |
130 | def ping(**payload):
131 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('ping', **payload)
132 | if data:
133 | real_ts = float(timestamp)
134 | now = float(time.time())
135 | relay = int(float(now - real_ts) * 1000)
136 | try:
137 | web_client.chat_update(
138 | channel=channel_id,
139 | ts=timestamp,
140 | text='',
141 | attachments=[
142 | {
143 | "color": "#f8f33a",
144 | "blocks": [
145 | {
146 | "type": "section",
147 | "text": {
148 | "type": "mrkdwn",
149 | "text": ":signal_strength: *Response Time:* {}ms".format(relay)
150 | }
151 | }
152 | ]
153 | }
154 | ]
155 | )
156 | except SlackApiError as e:
157 | bot.error(e)
158 |
159 | def msgstatus(**payload):
160 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('msgstatus', **payload)
161 | if data:
162 | if bot.msgstatus:
163 | bot.msgstatus = False
164 | try:
165 | web_client.chat_update(
166 | channel=channel_id,
167 | ts=timestamp,
168 | text="On-Message Status Change Disabled"
169 | )
170 | except SlackApiError as e:
171 | bot.error(e)
172 | else:
173 | bot.msgstatus = True
174 | try:
175 | web_client.chat_update(
176 | channel=channel_id,
177 | ts=timestamp,
178 | text="On-Message Status Change Enabled"
179 | )
180 | except SlackApiError as e:
181 | bot.error(e)
182 |
183 | def winfo(**payload):
184 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('winfo', **payload)
185 | if data:
186 | try:
187 | web_client.chat_update(
188 | channel=channel_id,
189 | ts=timestamp,
190 | text="Getting Workspace Info.."
191 | )
192 | except SlackApiError as e:
193 | bot.error(e)
194 | try:
195 | t_info = web_client.team_info()
196 | except SlackApiError as e:
197 | bot.error(e)
198 | try:
199 | chan_count = 0
200 | cursor = ""
201 | while True:
202 | c_info = web_client.conversations_list(limit=100,cursor=cursor)
203 | for _ in c_info['channels']:
204 | chan_count += 1
205 | if c_info.get('response_metadata').get("next_cursor"):
206 | cursor = c_info['response_metadata'].get("next_cursor")
207 | pass
208 | else:
209 | break
210 | except SlackApiError as e:
211 | bot.error(e)
212 |
213 | if t_info and chan_count:
214 | team = t_info['team']
215 | blocks = [
216 | {
217 | "type": "section",
218 | "text": {
219 | "type": "mrkdwn",
220 | "text": ":slack: *Team Info for {}:*".format(team['name'])
221 | },
222 | "accessory": {
223 | "type": "image",
224 | "image_url": team['icon']['image_230'],
225 | "alt_text": "Team Icon"
226 | }
227 | },
228 | {
229 | "type": "section",
230 | "fields": [
231 | {
232 | "type": "mrkdwn",
233 | "text": "*ID:* {}".format(team['id'])
234 | },
235 | {
236 | "type": "mrkdwn",
237 | "text": "*Domain:* {}.slack.com".format(team['domain'])
238 | },
239 | {
240 | "type": "mrkdwn",
241 | "text": "*E-Mail Format:* email@{}".format(team.get('email_domain') if team.get('email_domain') else "N/A")
242 | },
243 | {
244 | "type": "mrkdwn",
245 | "text": "*Enterprise Name:* {}".format(team.get('enterprise_name') if team.get('enterprise_name') else 'N/A')
246 | },
247 | {
248 | "type": "mrkdwn",
249 | "text": "*Channel Count:* {}".format(chan_count)
250 | }
251 | ]
252 | }
253 | ]
254 | try:
255 | web_client.chat_update(
256 | channel=channel_id,
257 | ts=timestamp,
258 | attachments=[
259 | {
260 | 'color': '#0f87ff',
261 | 'blocks': blocks
262 | }
263 | ]
264 | )
265 | except SlackApiError as e:
266 | bot.error(e)
267 |
268 |
269 |
270 | def uinfo(**payload):
271 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('uinfo', **payload)
272 | if data:
273 | if len(text_split) != 2:
274 | try:
275 | web_client.chat_update(
276 | channel=channel_id,
277 | ts=timestamp,
278 | text="Correct syntax is: `uinfo @user`"
279 | )
280 | except SlackApiError as e:
281 | bot.error(e)
282 | else:
283 | try:
284 | web_client.chat_update(
285 | channel=channel_id,
286 | ts=timestamp,
287 | text="Getting User Info.."
288 | )
289 | except SlackApiError as e:
290 | bot.error(e)
291 | try:
292 | user_list = client.users_list()
293 | except SlackApiError as e:
294 | bot.error(e)
295 | if user_list:
296 | query = text_split[1].split('@')[1].split('>')[0]
297 | match = None
298 | for wuser in user_list['members']:
299 | if wuser['id'] == query:
300 | match = wuser
301 | break
302 | if match:
303 | blocks = [
304 | {
305 | 'type': 'section',
306 | 'text': {
307 | 'type': 'mrkdwn',
308 | 'text': ':slack: *User Information:*'
309 | }
310 | },
311 | {
312 | 'type': 'section',
313 | 'text': {
314 | 'type': 'mrkdwn',
315 | 'text': '*Name:* {}\n*Status:* {}'.format(match['real_name'], match['profile'].get('status_text') if match['profile'].get('status_text') else 'N/A')
316 | },
317 | 'fields': [
318 | {
319 | 'type': 'mrkdwn',
320 | 'text': '*Title:* {}'.format(match['profile'].get('title') if match['profile'].get('title') else 'N/A')
321 | },
322 | {
323 | 'type': 'mrkdwn',
324 | 'text': '*Username:* {}'.format(match['name'])
325 | },
326 | {
327 | 'type': 'mrkdwn',
328 | 'text': '*E-Mail:* {}'.format(match['profile']['email'])
329 | },
330 | {
331 | 'type': 'mrkdwn',
332 | 'text': '*Timezone:* {}'.format(match['tz_label'])
333 | },
334 | {
335 | 'type': 'mrkdwn',
336 | 'text': '*Phone:* {}'.format(match['profile'].get('phone') if match['profile'].get('phone') else 'N/A')
337 | }
338 | ],
339 | 'accessory': {
340 | 'type': 'image',
341 | 'image_url': match['profile'].get('image_512'),
342 | 'alt_text': 'Profile Picture'
343 | }
344 | }
345 | ]
346 | try:
347 | web_client.chat_update(
348 | channel=channel_id,
349 | ts=timestamp,
350 | attachments=[{
351 | 'color': '#0f87ff',
352 | 'blocks': blocks
353 | }]
354 | )
355 | except SlackApiError as e:
356 | bot.error(e)
357 |
358 | else:
359 | try:
360 | web_client.chat_update(
361 | channel=channel_id,
362 | ts=timestamp,
363 | text="No User Found."
364 | )
365 | except SlackApiError as e:
366 | bot.error(e)
367 |
368 | def errors(**payload):
369 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('errors', **payload)
370 | if data:
371 | if len(bot.errors) > 0:
372 | blocks = [
373 | {
374 | 'type': 'section',
375 | 'text': {
376 | 'type': 'mrkdwn',
377 | 'text': '*Slacky Bot Errors:*'
378 | }
379 | }
380 | ]
381 | msg = ""
382 | for error in bot.errors:
383 | msg += str(error + '\n')
384 | blocks.append({
385 | 'type': 'section',
386 | 'text': {
387 | 'type': 'mrkdwn',
388 | 'text': '```' + msg + '```'
389 | }
390 | })
391 | try:
392 | web_client.chat_update(
393 | channel=channel_id,
394 | ts=timestamp,
395 | attachments=[{
396 | 'color': '#0f87ff',
397 | 'blocks': blocks
398 | }]
399 | )
400 | except SlackApiError as e:
401 | bot.error(e)
402 |
403 | else:
404 | try:
405 | web_client.chat_update(
406 | channel=channel_id,
407 | ts=timestamp,
408 | text='No Errors Triggered!'
409 | )
410 | except SlackApiError as e:
411 | bot.error(e)
412 |
413 |
414 | def animations(**payload):
415 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('ani', **payload)
416 | if data:
417 | if len(text_split) < 2:
418 | try:
419 | web_client.chat_delete(
420 | channel=channel_id,
421 | ts=timestamp
422 | )
423 | except SlackApiError as e:
424 | bot.error(e)
425 |
426 | else:
427 | if len(text_split) == 2:
428 | loop = 1
429 | elif len(text_split) == 3:
430 | loop = int(text_split[2])
431 | target_file = None
432 | for file in glob.glob('animations/*.txt'):
433 | if ntpath.basename(file).strip('.txt') == text_split[1]:
434 | target_file = os.path.realpath(file)
435 | if target_file:
436 | with open(target_file, 'r') as anif:
437 | full_ani = anif.read()
438 | lines = full_ani.split('\n')
439 | interval = lines[0]
440 | msgs = []
441 | tmp_msg = []
442 | for line in lines[1:]:
443 | if ''.join(line).startswith('[f#]'):
444 | line = '[f#]'
445 | if line != '[f#]':
446 | tmp_msg.append(line)
447 | else:
448 | msgs.append(tmp_msg)
449 | tmp_msg = []
450 | for i in range(loop):
451 | for msg in msgs:
452 | try:
453 | web_client.chat_update(
454 | channel=channel_id,
455 | ts=timestamp,
456 | text='```' + '\n'.join(msg) + '```'
457 | )
458 | except SlackApiError as e:
459 | bot.error(e)
460 |
461 | time.sleep(float(interval))
462 | else:
463 | try:
464 | web_client.chat_update(
465 | channel=channel_id,
466 | ts=timestamp,
467 | text="**No animation with that name found!**"
468 | )
469 | except SlackApiError as e:
470 | bot.error(e)
471 |
472 |
473 | def stats(**payload):
474 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('stats', **payload)
475 | if data:
476 | workspace = client.team_info()['team']['name']
477 | real_ts = float(timestamp)
478 | now = float(time.time())
479 | relay = int(float(now - real_ts) * 1000)
480 | if bot.needs_update:
481 | version_text = str(version) + " (Update Available)"
482 | else:
483 | version_text = str(version)
484 | blocks = [
485 | {
486 | "type": "section",
487 | "text": {
488 | "type": "mrkdwn",
489 | "text": ":slack: *Slacky Bot Statistics:*"
490 | }
491 | },
492 | {
493 | "type": "section",
494 | "fields": [
495 | {
496 | "type": "mrkdwn",
497 | "text": ":clock1: Uptime: *{}*".format(bot.get_uptime())
498 | },
499 | {
500 | "type": "mrkdwn",
501 | "text": ":bangbang: *Error Count*: *{}*".format(bot.error_count)
502 | },
503 | {
504 | "type": "mrkdwn",
505 | "text": ":keyboard: *Command Count*: *{}*".format(bot.command_count)
506 | },
507 | {
508 | "type": "mrkdwn",
509 | "text": ":warning: *Warning Count*: *{}*".format(bot.warning_count)
510 | },
511 | {
512 | "type": "mrkdwn",
513 | "text": ":computer: *Current Workspace*: *{}*".format(workspace)
514 | },
515 | {
516 | "type": "mrkdwn",
517 | "text": ":eyes: *Msgs Parsed*: *{}*".format(bot.message_count)
518 | },
519 | {
520 | "type": "mrkdwn",
521 | "text": ":signal_strength: *Response Time:* {}ms".format(relay)
522 | },
523 | {
524 | "type": "mrkdwn",
525 | "text": "*Version*: {}".format(version_text)
526 | }
527 | ]
528 | }
529 | ]
530 | try:
531 | web_client.chat_update(
532 | channel=channel_id,
533 | ts=timestamp,
534 | text="",
535 | attachments=[{
536 | 'color': '#0f87ff',
537 | 'blocks': blocks
538 | }]
539 | )
540 | except SlackApiError as e:
541 | bot.error(e)
542 |
543 | def customrscmd(**payload):
544 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('customrs', **payload)
545 | if data:
546 | if len(text_split) < 2:
547 | try:
548 | web_client.chat_update(
549 | channel=channel_id,
550 | text="Missing Arguments. Check the wiki for more information.",
551 | ts=timestamp
552 | )
553 | bot.warning_count += 1
554 | except SlackApiError as e:
555 | bot.error(e)
556 |
557 | else:
558 | action = text_split[1]
559 | if action == "add":
560 | if len(text_split) < 4:
561 | try:
562 | web_client.chat_update(
563 | channel=channel_id,
564 | text="Missing Arguments. Check the wiki for more information.",
565 | ts=timestamp
566 | )
567 | bot.warning_count += 1
568 | except SlackApiError as e:
569 | bot.error(e)
570 |
571 | else:
572 | ans = re.findall(r'["“‘\'](.*?)[\'’”"]', text)
573 | trigger = ans[0]
574 | reply = ans[1]
575 | is_strict = text_split[-1]
576 | if is_strict == "strict":
577 | is_strict = True
578 | else:
579 | is_strict = False
580 | custom_r = {
581 | 'trigger': trigger,
582 | 'reply': reply,
583 | 'is_strict': is_strict
584 | }
585 | customrs.add(custom_r)
586 | try:
587 | web_client.chat_update(
588 | channel=channel_id,
589 | text="Added Custom Reply. Trigger is \"{}\" and Reply will be \"{}\".\nStrict: {}".format(trigger, reply, is_strict),
590 | ts=timestamp
591 | )
592 | except SlackApiError as e:
593 | bot.error(e)
594 |
595 | elif action == "delete":
596 | if len(text_split) < 3:
597 | try:
598 | web_client.chat_update(
599 | channel=channel_id,
600 | text="Missing Arguments. Check the wiki for more information.",
601 | ts=timestamp
602 | )
603 | bot.warning_count += 1
604 | except SlackApiError as e:
605 | bot.error(e)
606 |
607 | else:
608 | num = text_split[2]
609 | try:
610 | customrs.delete(num)
611 | try:
612 | web_client.chat_update(
613 | channel=channel_id,
614 | text="Deleted Custom Reply.",
615 | ts=timestamp
616 | )
617 | except SlackApiError as e:
618 | bot.error(e)
619 | except IndexError as e:
620 | try:
621 | web_client.chat_update(
622 | channel=channel_id,
623 | text="No Custom Reply with That ID",
624 | ts=timestamp
625 | )
626 | except SlackApiError as e:
627 | bot.error(e)
628 | elif action == "list":
629 | blocks = []
630 | if len(customrs.custom_replies) > 0:
631 | for custom_reply in customrs.custom_replies:
632 | blocks.append({
633 | "type": "section",
634 | "text": {
635 | "type": "mrkdwn",
636 | "text": "*#*: {}\n*Trigger:* {}\n*Reply:* {}\n*Strict:* {}".format(customrs.custom_replies.index(custom_reply), custom_reply['trigger'], custom_reply['reply'], custom_reply['is_strict'])
637 | }
638 | })
639 | try:
640 | web_client.chat_update(
641 | channel=channel_id,
642 | blocks=blocks,
643 | ts=timestamp
644 | )
645 | except SlackApiError as e:
646 | bot.error(e)
647 |
648 | else:
649 | try:
650 | web_client.chat_update(
651 | channel=channel_id,
652 | text="No Custom Replies Set! Add some to your config or use the customrs command.",
653 | ts=timestamp
654 | )
655 | except SlackApiError as e:
656 | bot.error(e)
657 |
658 |
659 | def customrsd(**payload):
660 | bot.message_count += 1
661 | data = payload['data']
662 | channel_id = data['channel']
663 | user = data.get('user')
664 | timestamp = data.get('ts')
665 | text = data.get('text')
666 | if text:
667 | if user != config['user']:
668 | if not config['prefix'] in text:
669 | for custom_reply in customrs.custom_replies:
670 | if not custom_reply['is_strict']:
671 | if custom_reply['trigger'].lower() in text.lower():
672 | try:
673 | client.chat_postMessage(
674 | channel=channel_id,
675 | text=custom_reply['reply'],
676 | as_user=True,
677 | ts=timestamp
678 | )
679 | except SlackApiError as e:
680 | bot.error(e)
681 |
682 | else:
683 | if text.lower() == custom_reply['trigger'].lower():
684 | try:
685 | client.chat_postMessage(
686 | channel=channel_id,
687 | text=custom_reply['reply'],
688 | as_user=True,
689 | ts=timestamp
690 | )
691 | except SlackApiError as e:
692 | bot.error(e)
693 |
694 |
695 | def ascii(**payload):
696 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('ascii', **payload)
697 | if data:
698 | if len(text_split) < 2:
699 | try:
700 | web_client.chat_update(
701 | channel=channel_id,
702 | text="You need to feed a string to asciify!",
703 | ts=timestamp
704 | )
705 | except SlackApiError as e:
706 | bot.error(e)
707 |
708 | else:
709 | rest = ' '.join(text_split[1:])
710 | f = Figlet(font='slant')
711 | ascii_text = f.renderText(rest)
712 | try:
713 | web_client.chat_update(
714 | channel=channel_id,
715 | text="```{}```".format(ascii_text),
716 | ts=timestamp
717 | )
718 | except SlackApiError as e:
719 | bot.error(e)
720 |
721 |
722 | def status(**payload):
723 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('setstatus', **payload)
724 | if data:
725 | if len(text_split) < 3:
726 | print(Prefixes.warning + 'Missing Arguments! Read Help For Information')
727 | bot.warning_count += 1
728 | else:
729 | emoji = text_split[1]
730 | phrase = ' '.join(text_split[2:])
731 | try:
732 | web_client.users_profile_set(
733 | profile= {
734 | "status_text": phrase,
735 | "status_emoji": emoji,
736 | "status_expiration": 0
737 | })
738 | web_client.chat_update(
739 | channel=channel_id,
740 | text="Set Status Successfully!",
741 | ts=timestamp
742 | )
743 | except SlackApiError as e:
744 | bot.error(e)
745 |
746 | try:
747 | web_client.chat_update(
748 | channel=channel_id,
749 | text="Failed to set status.",
750 | ts=timestamp
751 | )
752 | except SlackApiError as e:
753 | bot.error(e)
754 |
755 |
756 | def setprefix(**payload):
757 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('setprefix', **payload)
758 | if data:
759 | if len(text_split) != 2:
760 | try:
761 | web_client.chat_update(
762 | channel=channel_id,
763 | text="Please only feed one argument for your prefix. You can repeat special chars to act as a longer prefix.",
764 | ts=timestamp
765 | )
766 | except SlackApiError as e:
767 | bot.error(e)
768 |
769 | else:
770 | prefix = text_split[1]
771 | config['prefix'] = prefix
772 | with open('config.json', 'r+') as file:
773 | obj = json.load(file)
774 | obj['prefix'] = prefix
775 | file.seek(0)
776 | json.dump(obj, file, indent=4)
777 | file.truncate()
778 | try:
779 | web_client.chat_update(
780 | channel=channel_id,
781 | text="Updated Prefix to: `{}`".format(prefix),
782 | ts=timestamp
783 | )
784 | except SlackApiError as e:
785 | bot.error(e)
786 |
787 |
788 | def shelp(**payload):
789 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('help', **payload)
790 | if data:
791 | attachments = [
792 | {
793 | "color": "#0f87ff",
794 | "blocks": [
795 | {
796 | "type": "section",
797 | "text": {
798 | "type": "mrkdwn",
799 | "text": "*Slacky Help Menu:*"
800 | },
801 | "fields": [
802 | {
803 | "type": "mrkdwn",
804 | "text": "*General*\n`{}help general`\n".format(config['prefix'])
805 | },
806 | {
807 | "type": "mrkdwn",
808 | "text": "*Fun*\n`{}help fun`\n".format(config['prefix'])
809 | },
810 | {
811 | "type": "mrkdwn",
812 | "text": "*Info*\n`{}help info`\n".format(config['prefix'])
813 | },
814 | {
815 | "type": "mrkdwn",
816 | "text": "*Custom Replies*\n`{}help cr`\n".format(config['prefix'])
817 | }
818 | ]
819 | }
820 | ]
821 | }]
822 | attachments_general = [{
823 | "color": "#d94c63",
824 | "blocks": [
825 | {
826 | "type": "section",
827 | "text": {
828 | "type": "mrkdwn",
829 | "text": "*General Commands:*"
830 | },
831 | "fields": [
832 | {
833 | "type": "mrkdwn",
834 | "text": "*ascii*\nConvert String To ASCII Art\n`{}ascii `\n".format(config['prefix'])
835 | },
836 | {
837 | "type": "mrkdwn",
838 | "text": "*ani*\nPlay Animation\n`{}ani [loops]`\n".format(config['prefix'])
839 | },
840 | {
841 | "type": "mrkdwn",
842 | "text": "*delete*\nDelete Last X Messages\n`{}delete `\n".format(config['prefix'])
843 | },
844 | {
845 | "type": "mrkdwn",
846 | "text": "*howdoi*\nGet Code Snippet From Stack Overflow\n`{}howdoi `\n".format(config['prefix'])
847 | },
848 | {
849 | "type": "mrkdwn",
850 | "text": "*react*\nReact to Last Message\n`{}react `\n".format(config['prefix'])
851 | },
852 | {
853 | "type": "mrkdwn",
854 | "text": "*reactrand*\nReact to Last Message with Random Emoji\n`{}reactrand`\n".format(config['prefix'])
855 | },
856 | {
857 | "type": "mrkdwn",
858 | "text": "*reactspam*\nSpam Last Message with Reactions\n`{}reactspam`\n".format(config['prefix'])
859 | },
860 | {
861 | "type": "mrkdwn",
862 | "text": "*setstatus*\nSet Your Slack Status\n`{}setstatus `\n".format(config['prefix'])
863 | }
864 | ]
865 | }
866 | ]
867 | }]
868 |
869 | attachments_crs = [{
870 | "color": "#b34cd9",
871 | "blocks": [
872 | {
873 | "type": "section",
874 | "text": {
875 | "type": "mrkdwn",
876 | "text": "*Custom Reply Commands:*"
877 | },
878 | "fields": [
879 | {
880 | "type": "mrkdwn",
881 | "text": "*customrs add*\nAdd Custom Reply\n`{}customrs add \"\" \"\" [strict|optional]`\n".format(config['prefix'])
882 | },
883 | {
884 | "type": "mrkdwn",
885 | "text": "*customrs delete*\nDelete Custom Reply\n`{}customrs delete `\n".format(config['prefix'])
886 | },
887 | {
888 | "type": "mrkdwn",
889 | "text": "*customrs list*\nList Custom Replies\n`{}customrs list`\n".format(config['prefix'])
890 | },
891 | {
892 | "type": "mrkdwn",
893 | "text": "*listener add*\nAdd a Listener\n`{}listener add `\n".format(config['prefix'])
894 | },
895 | {
896 | "type": "mrkdwn",
897 | "text": "*listener delete*\nDelete a Listener\n`{}listener delete `\n".format(config['prefix'])
898 | },
899 | ]
900 | }
901 | ]
902 | }]
903 | attachments_fun = [{
904 | "color": "#4cd992",
905 | "blocks": [
906 | {
907 | "type": "section",
908 | "text": {
909 | "type": "mrkdwn",
910 | "text": "*Fun Commands:*"
911 | },
912 | "fields": [
913 | {
914 | "type": "mrkdwn",
915 | "text": "*space*\nAdd a Space In Between Characters\n`{}space `\n".format(config['prefix'])
916 | },
917 | {
918 | "type": "mrkdwn",
919 | "text": "*msgstatus*\nEnable/Disable Random Emoji in Status on Msgs\n`{}msgstatus`".format(config['prefix'])
920 | },
921 | {
922 | "type": "mrkdwn",
923 | "text": "*subspace*\nSubstitute Every Space with an Emoji\n`{}subspace `\n".format(config['prefix'])
924 | },
925 | {
926 | "type": "mrkdwn",
927 | "text": "*shift*\nGeNeRaTe ShIfT tExT\n`{}shift `\n".format(config['prefix'])
928 | },
929 | {
930 | "type": "mrkdwn",
931 | "text": "*xkcd*\nGet The Daily xkcd Comic\n`{}xkcd`\n".format(config['prefix'])
932 | },
933 | {
934 | "type": "mrkdwn",
935 | "text": "*deepfry*\nDeepfry User Image or Image from URL\n`{}deepfry`\n*CUSTOM PLUGIN, MUST LOAD MANUALLY*".format(config['prefix'])
936 | }
937 | ]
938 | }
939 | ]
940 | }]
941 | attachments_info = [{
942 | "color": "#0f874a",
943 | "blocks": [
944 | {
945 | "type": "section",
946 | "text": {
947 | "type": "mrkdwn",
948 | "text": "*Info Commands:*"
949 | },
950 | "fields": [
951 | {
952 | "type": "mrkdwn",
953 | "text": "*help*\nDisplay Help Menu\n`{}help`\n".format(config['prefix'])
954 | },
955 | {
956 | "type": "mrkdwn",
957 | "text": "*info*\nDisplay Info About Bot\n`{}info`\n".format(config['prefix'])
958 | },
959 | {
960 | "type": "mrkdwn",
961 | "text": "*convinfo*\nGet Info About Chat/Channel\n`{}convinfo [#channel]`\n".format(config['prefix'])
962 | },
963 | {
964 | "type": "mrkdwn",
965 | "text": "*uinfo*\nGet Info About User\n`{}uinfo @user`\n".format(config['prefix'])
966 | },
967 | {
968 | "type": "mrkdwn",
969 | "text": "*winfo*\nGet Info About Workspace\n`{}winfo`\n".format(config['prefix'])
970 | },
971 | {
972 | "type": "mrkdwn",
973 | "text": "*stats*\nGet Stats About Bot\n`{}stats`\n".format(config['prefix'])
974 | },
975 | {
976 | "type": "mrkdwn",
977 | "text": "*ping*\nGet Response Time of Bot\n`{}ping`\n".format(config['prefix'])
978 | },
979 | {
980 | "type": "mrkdwn",
981 | "text": "*listener list*\nList All Listeners\n`{}listener list`\n".format(config['prefix'])
982 | }
983 | ]
984 | },
985 | ]
986 | }]
987 | if len(text_split) < 2:
988 | try:
989 | web_client.chat_update(
990 | channel=channel_id,
991 | ts=timestamp,
992 | text='',
993 | attachments=attachments
994 | )
995 | except SlackApiError as e:
996 | bot.error(e)
997 | else:
998 | if text_split[1] == 'general':
999 | try:
1000 | web_client.chat_update(
1001 | channel=channel_id,
1002 | ts=timestamp,
1003 | text='',
1004 | attachments=attachments_general
1005 | )
1006 | except SlackApiError as e:
1007 | bot.error(e)
1008 | elif text_split[1] == 'fun':
1009 | try:
1010 | web_client.chat_update(
1011 | channel=channel_id,
1012 | ts=timestamp,
1013 | text='',
1014 | attachments=attachments_fun
1015 | )
1016 | except SlackApiError as e:
1017 | bot.error(e)
1018 | elif text_split[1] == 'crs':
1019 | try:
1020 | web_client.chat_update(
1021 | channel=channel_id,
1022 | ts=timestamp,
1023 | text='',
1024 | attachments=attachments_crs
1025 | )
1026 | except SlackApiError as e:
1027 | bot.error(e)
1028 | elif text_split[1] == 'info':
1029 | try:
1030 | web_client.chat_update(
1031 | channel=channel_id,
1032 | ts=timestamp,
1033 | text='',
1034 | attachments=attachments_info
1035 | )
1036 | except SlackApiError as e:
1037 | bot.error(e)
1038 |
1039 | def reactrand(**payload):
1040 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('reactrand', **payload)
1041 | if data:
1042 | try:
1043 | web_client.chat_delete(
1044 | channel=channel_id,
1045 | ts=timestamp
1046 | )
1047 | except SlackApiError as e:
1048 | bot.error(e)
1049 |
1050 | conv_info = client.conversations_history(channel=channel_id, count=1)
1051 | latest_ts = conv_info['messages'][0]['ts']
1052 | try:
1053 | web_client.reactions_add(
1054 | channel=channel_id,
1055 | timestamp=latest_ts,
1056 | name=random.choice(emojis)
1057 | )
1058 | except SlackApiError as e:
1059 | bot.error(e)
1060 |
1061 |
1062 | def reactspam(**payload):
1063 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('reactspam', **payload)
1064 | if data:
1065 | try:
1066 | web_client.chat_delete(
1067 | channel=channel_id,
1068 | ts=timestamp
1069 | )
1070 | except SlackApiError as e:
1071 | bot.error(e)
1072 |
1073 | conv_info = client.conversations_history(channel=channel_id, count=1)
1074 | latest_ts = conv_info['messages'][0]['ts']
1075 | for _ in range(23):
1076 | try:
1077 | web_client.reactions_add(
1078 | channel=channel_id,
1079 | timestamp=latest_ts,
1080 | name=random.choice(emojis)
1081 | )
1082 | except SlackApiError as e:
1083 | bot.error(e)
1084 |
1085 |
1086 | def ud(**payload):
1087 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('ud', **payload)
1088 | if data:
1089 | if len(text_split) < 2:
1090 | web_client.chat_delete(
1091 | channel=channel_id,
1092 | ts=timestamp
1093 | )
1094 | print(Prefixes.warning + 'Missing Arguments! Read Help For Information')
1095 | bot.warning_count += 1
1096 | else:
1097 | api = 'http://urbanscraper.herokuapp.com/define/'
1098 | term = '+'.join(text_split[1:])
1099 | query = str(api + term)
1100 | res = httpx.get(query).json()
1101 | definition = res['definition']
1102 | url = res['url']
1103 | blocks = [
1104 | {
1105 | "type": "section",
1106 | "text": {
1107 | "type": "mrkdwn",
1108 | "text": "*Urban Dictionary Definition For:* {}\n\n{}\n\n<{}|Link To Entry>".format(term, definition, url)
1109 | }
1110 | }
1111 | ]
1112 | try:
1113 | web_client.chat_update(
1114 | channel=channel_id,
1115 | ts=timestamp,
1116 | blocks=blocks
1117 | )
1118 | except SlackApiError as e:
1119 | bot.error(e)
1120 |
1121 |
1122 | def space(**payload):
1123 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('space', **payload)
1124 | if data:
1125 | if len(text_split) < 2:
1126 | web_client.chat_delete(
1127 | channel=channel_id,
1128 | ts=timestamp
1129 | )
1130 | print(Prefixes.warning + 'Missing Arguments! Read Help For Information')
1131 | bot.warning_count += 1
1132 | else:
1133 | rest = ' '.join(text_split[1:])
1134 | new_string = ""
1135 | for char in rest:
1136 | new_string += str(char + " ")
1137 | try:
1138 | web_client.chat_update(
1139 | channel=channel_id,
1140 | ts=timestamp,
1141 | text=new_string
1142 | )
1143 | except SlackApiError as e:
1144 | bot.error(e)
1145 |
1146 | def sub_space(**payload):
1147 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('subspace', **payload)
1148 | if data:
1149 | if len(text_split) < 3:
1150 | web_client.chat_delete(
1151 | channel=channel_id,
1152 | ts=timestamp
1153 | )
1154 | print(Prefixes.warning + 'Missing Arguments! Read Help For Information')
1155 | bot.warning_count += 1
1156 | else:
1157 | emoji = text_split[1]
1158 | rest = ' '.join(text_split[2:])
1159 | rest = rest.replace(' ', ' {} '.format(emoji))
1160 | try:
1161 | web_client.chat_update(
1162 | channel=channel_id,
1163 | text=rest,
1164 | ts=timestamp
1165 | )
1166 | except SlackApiError as e:
1167 | bot.error(e)
1168 |
1169 |
1170 | def delete(**payload):
1171 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('delete', **payload)
1172 | if data:
1173 | web_client.chat_delete(
1174 | channel=channel_id,
1175 | ts=timestamp
1176 | )
1177 | if len(text_split) < 2:
1178 | print(Prefixes.warning + 'Missing Arguments! Read Help For Information')
1179 | bot.warning_count += 1
1180 | else:
1181 | print(Prefixes.event + 'Ran Command: delete')
1182 | bot.command_count += 1
1183 | msgs = int(text_split[1])
1184 | conv_hist = web_client.conversations_history(channel=channel_id, count=msgs if msgs <= 100 else 100)
1185 | msg_ts = []
1186 | for i in conv_hist['messages']:
1187 | for k, v in i.items():
1188 | if k == "user" and v == config['user']:
1189 | msg_ts.append(i['ts'])
1190 | for ts in msg_ts:
1191 | try:
1192 | web_client.chat_delete(
1193 | channel=channel_id,
1194 | ts=ts
1195 | )
1196 | except SlackApiError as e:
1197 | bot.error(e)
1198 |
1199 |
1200 |
1201 | def shift(**payload):
1202 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('shift', **payload)
1203 | if data:
1204 | if len(text_split) < 2:
1205 | web_client.chat_delete(
1206 | channel=channel_id,
1207 | ts=timestamp
1208 | )
1209 | print(Prefixes.error + 'Missing Arguments! Please read the help menu for more info!')
1210 | bot.warning_count += 1
1211 | else:
1212 | rest = ' '.join(text_split[1:])
1213 | new_text = ""
1214 | count = 0
1215 | for char in rest:
1216 | if count == 0:
1217 | new_text += char.upper()
1218 | count = 1
1219 | else:
1220 | new_text += char.lower()
1221 | count = 0
1222 | try:
1223 | web_client.chat_update(
1224 | channel=channel_id,
1225 | text=new_text,
1226 | ts=timestamp
1227 | )
1228 | except SlackApiError as e:
1229 | bot.error(e)
1230 |
1231 |
1232 | def info(**payload):
1233 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('info', **payload)
1234 | if data:
1235 | try:
1236 | attachment = [
1237 | {
1238 | "color": "#0f87ff",
1239 | "blocks": [
1240 | {
1241 | "type": "section",
1242 | "text": {
1243 | "type": "mrkdwn",
1244 | "text": """Running :slack: *Slacky* v{} by
1245 | *Source Code*:
1246 | *Wiki*: """.format(version, config['prefix'])
1247 | }
1248 | }
1249 | ]
1250 | }
1251 | ]
1252 | web_client.chat_update(
1253 | channel=channel_id,
1254 | ts=timestamp,
1255 | text="",
1256 | attachments=attachment
1257 | )
1258 | except SlackApiError as e:
1259 | bot.error(e)
1260 |
1261 |
1262 | def howdoicmd(**payload):
1263 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('howdoi', **payload)
1264 | if data:
1265 | if len(text_split) < 2:
1266 | web_client.chat_delete(
1267 | channel=channel_id,
1268 | ts=timestamp
1269 | )
1270 | print(Prefixes.error + 'Missing Arguments! Please read the help menu for more info!')
1271 | bot.warning_count += 1
1272 | try:
1273 | web_client.chat_update(
1274 | channel=channel_id,
1275 | text="Finding the answer to that...",
1276 | ts=timestamp
1277 | )
1278 | except SlackApiError as e:
1279 | bot.error(e)
1280 |
1281 | parser = howdoi.get_parser()
1282 | args = vars(parser.parse_args(text_split[1:]))
1283 | output = howdoi.howdoi(args)
1284 | try:
1285 | web_client.chat_update(
1286 | channel=channel_id,
1287 | text="```{}```".format(output),
1288 | ts=timestamp
1289 | )
1290 | except SlackApiError as e:
1291 | bot.error(e)
1292 |
1293 |
1294 | def heartbeat(**payload):
1295 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('heartbeat', **payload)
1296 | if data:
1297 | try:
1298 | web_client.chat_update(
1299 | channel=channel_id,
1300 | text="I'm Alive!",
1301 | ts=timestamp
1302 | )
1303 | except SlackApiError as e:
1304 | bot.error(e)
1305 |
1306 |
1307 | def react(**payload):
1308 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('react', **payload)
1309 | if data:
1310 | try:
1311 | web_client.chat_delete(
1312 | channel=channel_id,
1313 | ts=timestamp
1314 | )
1315 | except SlackApiError as e:
1316 | bot.error(e)
1317 |
1318 | emoji = text_split[1]
1319 | print(Prefixes.event + 'Ran Command: react')
1320 | bot.command_count += 1
1321 | conv_info = client.conversations_info(channel=channel_id)
1322 | latest = conv_info['channel']['latest']
1323 | latest_ts = latest['ts']
1324 | try:
1325 | web_client.reactions_add(
1326 | channel=channel_id,
1327 | timestamp=latest_ts,
1328 | name=emoji.replace(':', '')
1329 | )
1330 | except SlackApiError as e:
1331 | bot.error(e)
1332 |
1333 |
1334 | def listenerd(**payload):
1335 | data = payload['data']
1336 | channel_id = data['channel']
1337 | text = data.get('text')
1338 | if bot.msgstatus:
1339 | try:
1340 | client.users_profile_set(
1341 | profile={
1342 | 'status_emoji': ':' + str(random.choice(emojis)) + ':',
1343 | 'status_text': client.users_profile_get(user=config['user'])['profile'].get('status_text'),
1344 | 'status_expiration': 0
1345 | }
1346 | )
1347 | except SlackApiError as e:
1348 | pass
1349 | if text:
1350 | if not config['prefix'] in text:
1351 | if len(listener.listeners) >= 1:
1352 | if any(x in text for x in listener.listeners):
1353 | print(Prefixes.event + 'Listener Triggered! Message:', text, '| Channel ID:', channel_id)
1354 |
1355 | def listenercmd(**payload):
1356 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('listener', **payload)
1357 | if data:
1358 | if len(text_split) == 1:
1359 | print(Prefixes.warning + 'Missing Arguments! Read Help For Information')
1360 | bot.warning_count += 1
1361 | else:
1362 | action = text_split[1]
1363 | phrase = ' '.join(text_split[2:])
1364 | if action == 'add':
1365 | listener.add(phrase)
1366 | print(Prefixes.event + 'Listener Added:', phrase)
1367 | bot.command_count += 1
1368 | try:
1369 | web_client.chat_update(
1370 | channel=channel_id,
1371 | text="`{}` added to listeners.".format(phrase),
1372 | ts=timestamp
1373 | )
1374 | except SlackApiError as e:
1375 | bot.error(e)
1376 |
1377 | elif action == 'list':
1378 | bot.command_count += 1
1379 | listeners = ""
1380 | for ear in listener.listeners:
1381 | listeners += str(ear + '\n')
1382 | try:
1383 | web_client.chat_update(
1384 | channel=channel_id,
1385 | text="```{}```".format(listeners),
1386 | ts=timestamp
1387 | )
1388 | except SlackApiError as e:
1389 | bot.error(e)
1390 |
1391 | elif action == 'delete':
1392 | listener.delete(phrase)
1393 | bot.command_count += 1
1394 | print(Prefixes.event + 'Listener Deleted:', phrase)
1395 | try:
1396 | web_client.chat_update(
1397 | channel=channel_id,
1398 | text="`{}` removed from listeners.".format(phrase),
1399 | ts=timestamp
1400 | )
1401 | except SlackApiError as e:
1402 | bot.error(e)
1403 |
1404 |
1405 | def xkcd(**payload):
1406 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('xkcd', **payload)
1407 | if data:
1408 | res = httpx.get('https://xkcd.com/info.0.json').json()
1409 | link = res['img']
1410 | alt_text = res['alt']
1411 | try:
1412 | web_client.chat_update(
1413 | channel=channel_id,
1414 | blocks=[
1415 | {
1416 | "type": "image",
1417 | "title": {
1418 | "type": "plain_text",
1419 | "text": alt_text,
1420 | "emoji": True
1421 | },
1422 | "image_url": link,
1423 | "alt_text": alt_text
1424 | }
1425 | ],
1426 | ts=timestamp
1427 | )
1428 | except SlackApiError as e:
1429 | bot.error(e)
1430 |
1431 | def convinfo(**payload):
1432 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('convinfo', **payload)
1433 | if data:
1434 | try:
1435 | web_client.chat_update(
1436 | channel=channel_id,
1437 | ts=timestamp,
1438 | text="This may take a while..."
1439 | )
1440 | except SlackApiError as e:
1441 | bot.error(e)
1442 | if len(text_split) == 2:
1443 | if '#' in text_split[1]:
1444 | fsplit = text_split[1].split('|')[0].split('#')[1]
1445 | nsplit = text_split[1].split('|')[1].strip('>')
1446 | print(fsplit)
1447 | bot.command_count += 1
1448 | messages = []
1449 | cursor = ""
1450 | while True:
1451 | try:
1452 | hist = client.conversations_history(channel=fsplit, limit=100, cursor=cursor)
1453 | for i in hist['messages']:
1454 | messages.append(i)
1455 | if hist['has_more']:
1456 | cursor = hist['response_metadata']['next_cursor']
1457 | pass
1458 | else:
1459 | break
1460 | except SlackApiError as e:
1461 | bot.error(e)
1462 | lols = 0
1463 | lmaos = 0
1464 | shits = 0
1465 | fucks = 0
1466 | msgs = 0
1467 | for message in messages:
1468 | msgs += 1
1469 | if str('lol') in message['text']:
1470 | lols += 1
1471 | elif str('lmao') in message['text']:
1472 | lmaos += 1
1473 | elif str('shit') in message['text']:
1474 | shits += 1
1475 | elif str('fuck') in message['text']:
1476 | fucks += 1
1477 | else:
1478 | pass
1479 | blocks = [
1480 | {
1481 | "type": "section",
1482 | "text": {
1483 | "type": "mrkdwn",
1484 | "text": ":slack: *Conversation History for {}:*".format(nsplit.capitalize())
1485 | }
1486 | },
1487 | {
1488 | 'type': 'section',
1489 | 'fields': [
1490 | {
1491 | 'type': 'mrkdwn',
1492 | 'text': '*# of LOLs:* {}'.format(lols)
1493 | },
1494 | {
1495 | 'type': 'mrkdwn',
1496 | 'text': '*# of LMAOs:* {}'.format(lmaos)
1497 | },
1498 | {
1499 | 'type': 'mrkdwn',
1500 | 'text': '*# of S--ts:* {}'.format(shits)
1501 | },
1502 | {
1503 | 'type': 'mrkdwn',
1504 | 'text': '*# of F--ks:* {}'.format(fucks)
1505 | },
1506 | {
1507 | 'type': 'mrkdwn',
1508 | 'text': '*Total Messages:* {}'.format(msgs)
1509 | }
1510 | ]
1511 | }
1512 | ]
1513 | try:
1514 | web_client.chat_update(
1515 | channel=channel_id,
1516 | ts=timestamp,
1517 | text='',
1518 | attachments=[
1519 | {
1520 | 'color': '#0f87ff',
1521 | 'blocks': blocks
1522 | }
1523 | ]
1524 | )
1525 | except SlackApiError as e:
1526 | bot.error(e)
1527 | else:
1528 | try:
1529 | web_client.chat_update(
1530 | channel=channel_id,
1531 | ts=timestamp,
1532 | text='Error. Bad Channel!'
1533 | )
1534 | except SlackApiError as e:
1535 | bot.error(e)
1536 | else:
1537 | bot.command_count += 1
1538 | messages = []
1539 | cursor = ""
1540 | while True:
1541 | try:
1542 | hist = client.conversations_history(channel=channel_id, limit=100, cursor=cursor)
1543 | for i in hist['messages']:
1544 | messages.append(i)
1545 | if hist['has_more']:
1546 | cursor = hist['response_metadata']['next_cursor']
1547 | pass
1548 | else:
1549 | break
1550 | except SlackApiError as e:
1551 | bot.error(e)
1552 | lols = 0
1553 | lmaos = 0
1554 | shits = 0
1555 | fucks = 0
1556 | msgs = 0
1557 | for message in messages:
1558 | msgs += 1
1559 | if str('lol') in message['text']:
1560 | lols += 1
1561 | elif str('lmao') in message['text']:
1562 | lmaos += 1
1563 | elif str('shit') in message['text']:
1564 | shits += 1
1565 | elif str('fuck') in message['text']:
1566 | fucks += 1
1567 | else:
1568 | pass
1569 | blocks = [
1570 | {
1571 | "type": "section",
1572 | "text": {
1573 | "type": "mrkdwn",
1574 | "text": ":slack: *Conversation History:*"
1575 | }
1576 | },
1577 | {
1578 | 'type': 'section',
1579 | 'fields': [
1580 | {
1581 | 'type': 'mrkdwn',
1582 | 'text': '*# of LOLs:* {}'.format(lols)
1583 | },
1584 | {
1585 | 'type': 'mrkdwn',
1586 | 'text': '*# of LMAOs:* {}'.format(lmaos)
1587 | },
1588 | {
1589 | 'type': 'mrkdwn',
1590 | 'text': '*# of S--ts:* {}'.format(shits)
1591 | },
1592 | {
1593 | 'type': 'mrkdwn',
1594 | 'text': '*# of F--ks:* {}'.format(fucks)
1595 | },
1596 | {
1597 | 'type': 'mrkdwn',
1598 | 'text': '*Total Messages:* {}'.format(msgs)
1599 | }
1600 | ]
1601 | }
1602 | ]
1603 | try:
1604 | web_client.chat_update(
1605 | channel=channel_id,
1606 | ts=timestamp,
1607 | attachments=[{
1608 | 'color': '#0f87ff',
1609 | 'blocks': blocks
1610 | }]
1611 | )
1612 | except SlackApiError as e:
1613 | bot.error(e)
--------------------------------------------------------------------------------
/slacky/plugins/custom/__init__.py:
--------------------------------------------------------------------------------
1 | # Load Plugins Here
2 | from .example import custom_example
3 | # Uncomment to load Deepfry Plugin
4 | # from .deepfry import deepfry
5 | from .stockpic import stockpic
--------------------------------------------------------------------------------
/slacky/plugins/custom/deepfry/__init__.py:
--------------------------------------------------------------------------------
1 | from .plugin import deepfry
--------------------------------------------------------------------------------
/slacky/plugins/custom/deepfry/deepfrylogic.py:
--------------------------------------------------------------------------------
1 | from collections import namedtuple
2 | from io import BytesIO
3 | import math
4 | import pkgutil
5 | from typing import Tuple
6 |
7 | from PIL import Image, ImageOps, ImageEnhance
8 | from cv2.data import haarcascades
9 | import cv2
10 | import os
11 | import numpy
12 |
13 | __all__ = ('Colour', 'ColourTuple', 'DefaultColours', 'deepfry')
14 |
15 | Colour = Tuple[int, int, int]
16 | ColourTuple = Tuple[Colour, Colour]
17 |
18 |
19 | class DefaultColours:
20 | """Default colours provided for deepfrying"""
21 | red = ((254, 0, 2), (255, 255, 15))
22 | blue = ((36, 113, 229), (255,) * 3)
23 |
24 | face_cascade = cv2.CascadeClassifier(os.getcwd() + '/slacky/plugins/custom/deepfry/haarcascade_frontalface_default.xml')
25 | eye_cascade = cv2.CascadeClassifier(os.getcwd() + '/slacky/plugins/custom/deepfry/haarcascade_eye.xml')
26 | flare_img = Image.open(BytesIO(pkgutil.get_data(__package__, 'flare.png')))
27 |
28 | FlarePosition = namedtuple('FlarePosition', ['x', 'y', 'size'])
29 |
30 |
31 | def deepfryy(img=None, colours= DefaultColours.red, flares= True):
32 | """
33 | Deepfry a given image.
34 |
35 | Parameters
36 | ----------
37 | img : `Image`
38 | Image to manipulate.
39 | colours : `ColourTuple`, optional
40 | A tuple of the colours to apply on the image.
41 | flares : `bool`, optional
42 | Whether or not to try and detect faces for applying lens flares.
43 |
44 | Returns
45 | -------
46 | `Image`
47 | Deepfried image.
48 | """
49 | img = img.copy().convert('RGB')
50 | flare_positions = []
51 |
52 | if flares:
53 | opencv_img = cv2.cvtColor(numpy.array(img), cv2.COLOR_RGB2GRAY)
54 |
55 | faces = face_cascade.detectMultiScale(
56 | opencv_img,
57 | scaleFactor=1.3,
58 | minNeighbors=5,
59 | minSize=(30, 30),
60 | flags=cv2.CASCADE_SCALE_IMAGE
61 | )
62 |
63 | for (x, y, w, h) in faces:
64 | face_roi = opencv_img[y:y+h, x:x+w] # Get region of interest (detected face)
65 |
66 | eyes = eye_cascade.detectMultiScale(face_roi)
67 |
68 | for (ex, ey, ew, eh) in eyes:
69 | eye_corner = (ex + ew / 2, ey + eh / 2)
70 | flare_size = eh if eh > ew else ew
71 | flare_size *= 4
72 | corners = [math.floor(x) for x in eye_corner]
73 | eye_corner = FlarePosition(*corners, flare_size)
74 |
75 | flare_positions.append(eye_corner)
76 |
77 | # Crush image to hell and back
78 | img = img.convert('RGB')
79 | width, height = img.width, img.height
80 | img = img.resize((int(width ** .75), int(height ** .75)), resample=Image.LANCZOS)
81 | img = img.resize((int(width ** .88), int(height ** .88)), resample=Image.BILINEAR)
82 | img = img.resize((int(width ** .9), int(height ** .9)), resample=Image.BICUBIC)
83 | img = img.resize((width, height), resample=Image.BICUBIC)
84 | img = ImageOps.posterize(img, 4)
85 |
86 | # Generate colour overlay
87 | r = img.split()[0]
88 | r = ImageEnhance.Contrast(r).enhance(2.0)
89 | r = ImageEnhance.Brightness(r).enhance(1.5)
90 |
91 | r = ImageOps.colorize(r, colours[0], colours[1])
92 |
93 | # Overlay red and yellow onto main image and sharpen the hell out of it
94 | img = Image.blend(img, r, 0.75)
95 | img = ImageEnhance.Sharpness(img).enhance(100.0)
96 |
97 | # Apply flares on any detected eyes
98 | for flare in flare_positions:
99 | flare_transformed = flare_img.copy().resize((flare.size,) * 2, resample=Image.BILINEAR)
100 | img.paste(flare_transformed, (flare.x, flare.y), flare_transformed)
101 |
102 | return img
--------------------------------------------------------------------------------
/slacky/plugins/custom/deepfry/flare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M4cs/Slacky/992dc75297295878ba37fa64a5ff44c50beac890/slacky/plugins/custom/deepfry/flare.png
--------------------------------------------------------------------------------
/slacky/plugins/custom/deepfry/plugin.py:
--------------------------------------------------------------------------------
1 | from slacky import client, config, check_user, bot, version, Prefixes
2 | from .deepfrylogic import deepfryy
3 | from slack.errors import SlackApiError
4 | from PIL import Image
5 | from io import BytesIO
6 | import httpx, os
7 |
8 | def cmd_setup(command, **payload):
9 | data = payload['data']
10 | channel_id = data['channel']
11 | user = data.get('user')
12 | timestamp = data.get('ts')
13 | web_client = client
14 | text = data.get('text')
15 | if text and check_user(user):
16 | text_split = text.split(' ')
17 | cmd = text_split[0]
18 | if cmd == config['prefix'] + command:
19 | print(Prefixes.event + 'Ran Command: {}'.format(command))
20 | bot.command_count += 1
21 | return data, channel_id, user, timestamp, web_client, text, text_split
22 | else:
23 | return None, None, None, None, None, None, None
24 | else:
25 | return None, None, None, None, None, None, None
26 |
27 | def deepfry(**payload):
28 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('deepfry', **payload)
29 | if data:
30 | if len(text_split) < 2:
31 | try:
32 | web_client.chat_update(
33 | channel=channel_id,
34 | ts=timestamp,
35 | text="Missing User to Deepfry!"
36 | )
37 | except SlackApiError as e:
38 | bot.error(e)
39 | else:
40 | if 'http' in text_split[1]:
41 | image = httpx.get(text_split[1].strip('<').strip('>')).content
42 | img = Image.open(BytesIO(image))
43 | img = deepfryy(img=img, flares=False)
44 | img.save('./tmp.jpg')
45 | try:
46 | web_client.chat_delete(
47 | channel=channel_id,
48 | ts=timestamp
49 | )
50 | with open('tmp.jpg', 'rb') as imdata:
51 | r = httpx.post('https://slack.com/api/files.upload', data={'token': config['token'], 'channels': [channel_id], 'title': 'DEEPFRY', 'as_user': 'True'}, files={'file': imdata})
52 | os.remove('tmp.jpg')
53 | except SlackApiError as e:
54 | bot.error(e)
55 | else:
56 | try:
57 | user_list = client.users_list()
58 | except SlackApiError as e:
59 | bot.error(e)
60 | if user_list:
61 | query = text_split[1].split('@')[1].split('>')[0]
62 | match = None
63 | for wuser in user_list['members']:
64 | if wuser['id'] == query:
65 | match = wuser
66 | break
67 | if match:
68 | url = match['profile'].get('image_512')
69 | if url:
70 | image = httpx.get(match['profile'].get('image_512')).content
71 | img = Image.open(BytesIO(image))
72 | img = deepfryy(img=img)
73 | img.save('./tmp.jpg')
74 | try:
75 | web_client.chat_delete(
76 | channel=channel_id,
77 | ts=timestamp
78 | )
79 | with open('tmp.jpg', 'rb') as imdata:
80 | r = httpx.post('https://slack.com/api/files.upload', data={'token': config['token'], 'channels': [channel_id], 'title': 'DEEPFRY', 'as_user': 'True'}, files={'file': imdata})
81 | except SlackApiError as e:
82 | bot.error(e)
--------------------------------------------------------------------------------
/slacky/plugins/custom/example.py:
--------------------------------------------------------------------------------
1 | from slacky import client, config, Prefixes, check_user
2 | from slack.errors import SlackApiError
3 |
4 | def custom_example(**payload):
5 | # Get Data from Payload
6 | data = payload['data']
7 | channel_id = data['channel'] # Get Channel ID
8 | user = data.get('user') # Get User
9 | timestamp = data['ts'] # Get msg Timestamp
10 | if check_user(user): # Check if User == You
11 | web_client = client # Init Client
12 | text = data.get('text') # Get Text
13 | # Check for Command Here
14 | if text:
15 | text_split = text.split(' ')
16 | cmd = text_split[0]
17 | if cmd == config['prefix'] + 'example':
18 | # Command has been triggered
19 | print(Prefixes.event + 'Ran Command: example')
20 | # Do your logic here and then update the message at the end below.
21 | try:
22 | web_client.chat_update(
23 | channel=channel_id,
24 | ts=timestamp,
25 | text="This command is an example custom command."
26 | )
27 | except SlackApiError as e:
28 | print(Prefixes.error + str(e))
--------------------------------------------------------------------------------
/slacky/plugins/custom/stockpic/__init__.py:
--------------------------------------------------------------------------------
1 | from .plugin import *
--------------------------------------------------------------------------------
/slacky/plugins/custom/stockpic/plugin.py:
--------------------------------------------------------------------------------
1 | from slacky import config, bot, Prefixes
2 | from slacky.plugins import cmd_setup
3 | from slack.errors import SlackApiError
4 | import requests, json, random
5 |
6 |
7 | print(Prefixes.start + "Loading Shutterstock Plugin...")
8 |
9 | def stockpic(**payload):
10 | data, channel_id, user, timestamp, web_client, text, text_split = cmd_setup('stockpic', **payload)
11 | if data:
12 | if config.get('shutterstock_key') and config.get('shutterstock_secret'):
13 | try:
14 | web_client.chat_update(
15 | channel=channel_id,
16 | ts=timestamp,
17 | text="Finding an Image..."
18 | )
19 | except SlackApiError as e:
20 | bot.error(e)
21 | res = requests.get('https://api.shutterstock.com/v2/images/search?query={}'.format('+'.join(text_split[1:])), auth=requests.auth.HTTPBasicAuth(config['shutterstock_key'], config['shutterstock_secret'])).json()
22 | images = res['data']
23 | image_links = []
24 | for image in images:
25 | image_links.append(image['assets']['preview']['url'])
26 | link = random.choice(image_links)
27 | try:
28 | web_client.chat_update(
29 | channel=channel_id,
30 | ts=timestamp,
31 | text='',
32 | attachments=[
33 | {
34 | 'color': '#0a85f2',
35 | 'blocks': [
36 | {
37 | 'type': 'image',
38 | 'title': {
39 | 'type': 'plain_text',
40 | 'text': ' '.join(text_split[1:]),
41 | 'emoji': True
42 | },
43 | 'image_url': link,
44 | 'alt_text': 'Stock Image'
45 | }
46 | ]
47 | }
48 | ]
49 | )
50 | except SlackApiError as e:
51 | bot.error(e)
52 | else:
53 | try:
54 | web_client.chat_update(
55 | channel=channel_id,
56 | ts=timestamp,
57 | text="Add `shutterstock_key` and `shutterstock_secret` to config with your credentials!"
58 | )
59 | except SlackApiError as e:
60 | bot.error(e)
--------------------------------------------------------------------------------
/version.txt:
--------------------------------------------------------------------------------
1 | 2.0.0
--------------------------------------------------------------------------------