diff --git a/setup.py b/setup.py index 3704032..94c8ccc 100755 --- a/setup.py +++ b/setup.py @@ -62,11 +62,9 @@ def parse_readme(): ], packages=[ 'sirbot.pythondev', - 'sirbot.pythondev.slack' ], package_dir={ 'sirbot.pythondev': 'sirbot/pythondev', - 'sirbot.pythondev.slack': 'sirbot/pythondev/slack', }, # To provide executable scripts, use entry points in preference to the # "scripts" keyword. Entry points provide cross-platform support and diff --git a/sirbot.yml b/sirbot.yml index 5e87381..e2b3f36 100644 --- a/sirbot.yml +++ b/sirbot.yml @@ -31,6 +31,54 @@ sqlite: pythondev: priority: 1 + candy: + trigger: ":bdfl:" + githhub: + channel: community_projects + python_jobs: + looking: > + *Weekly who's looking thread !* + + If you are looking for a job please post your resume in this thread. + looking_tips: > + We are an international community so please include your location and + if you are willing to relocate. + hiring: > + *Weekly who's hiring thread !* + + If you are looking for someone please post your job in this thread. + hiring_tips: > + We are an international community so please include your company + location and policy on remote employees. A good post should include the + name of the company, the location, on-site/remote and a quick summary + of the job. + digital_ocean: + url: https://digitalocean.com + refferral: https://m.do.co/c/457f0988c477 + msg: > + Here at Python Developers we host our website and Slack bot on + <{}|Digital Ocean>. If you are planning on using Digital Ocean, please + use our <{}|referral code>. You get 10 USD, while helping support the + community by contributing to hosting fees for our site and + <@sirbotalot>! + files: + msg: > + Click <{}|here> to access the {}. + intro_doc: + name: Intro doc + url: 'https://github.com/pyslackers/community/blob/master/introduction.md' + what_to_do: + name: What to do doc + url: 'https://pythondev.slack.com/files/ndevox/F4A137J0J/What_to_do_next_on_your_Python_journey' + join: > + Welcome to the community :tada: + + We are glad that you have decided to join us. We have documented a few + things in the <{}|intro doc> to help you along from the beginning because + we are grand believers in the Don't Repeat Yourself principle, and it just + seems so professional!\n\n + + May your :taco:s be plentiful! logging: version: 1 diff --git a/sirbot/pythondev/__init__.py b/sirbot/pythondev/__init__.py index 02b3649..ee55a36 100644 --- a/sirbot/pythondev/__init__.py +++ b/sirbot/pythondev/__init__.py @@ -1,8 +1,8 @@ from sirbot.core import hookimpl, Plugin -from .github import add_to_github -from .slack import add_to_slack -from .scheduler import add_to_scheduler +from .github import GitHubEndpoint +from .scheduler import SchedulerJobs +from .slack import SlackEndpoint @hookimpl @@ -31,15 +31,15 @@ async def start(self): if 'scheduler' in self._facades: scheduler_facade = self._facades.get('scheduler') - add_to_scheduler(scheduler_facade) + SchedulerJobs(self._config).add(scheduler_facade) if 'github' in self._facades: github_facade = self._facades.get('github') - add_to_github(github_facade) + GitHubEndpoint(self._config).add(github_facade) if 'slack' in self._facades: slack_facade = self._facades.get('slack') - add_to_slack(slack_facade) + SlackEndpoint(self._config).add(slack_facade) self._started = True diff --git a/sirbot/pythondev/github.py b/sirbot/pythondev/github.py index 7eab911..68db403 100644 --- a/sirbot/pythondev/github.py +++ b/sirbot/pythondev/github.py @@ -5,92 +5,100 @@ logger = logging.getLogger(__name__) -def add_to_github(github): - github.add_event('issues', issues) - github.add_event('pull_request', pull_request) - - -async def issues(event, facades): - att = None - - if event['action'] == 'opened': - att = issue_format(event, 'good') - elif event['action'] == 'closed': - att = issue_format(event, 'danger') - - if att: - slack = facades.get('slack') - channel = await slack.channels.get(name='community_projects') - msg = SlackMessage(to=channel) - msg.attachments.append(att) - await slack.send(msg) - - -def issue_format(event, color): - att = Attachment( - fallback='issue {}'.format(event['action']), - color=color, - text=event['issue']['body'], - title='Issue {action} in <{repo_url}|{name}>: <{url}|{title}>'.format( - repo_url=event['repository']['html_url'], - url=event['issue']['html_url'], - name=event['repository']['name'], - action=event['action'], - title=event['issue']['title'] - ), - author_icon=event['sender']['avatar_url'], - author_name=event['sender']['login'], - author_link=event['sender']['html_url'], - footer=', '.join(label['name'] for label in event['issue']['labels']) - ) - - return att - - -async def pull_request(event, facades): - att = None - - if event['action'] == 'opened': - data = {'color': 'good', 'action': 'opened'} - att = pull_request_format(event, data) - elif event['action'] == 'closed': - if event['pull_request']['merged']: - data = {'color': '#6f42c1', 'action': 'merged'} - else: - data = {'color': 'danger', 'action': 'closed'} - - att = pull_request_format(event, data) - - if att: - slack = facades.get('slack') - channel = await slack.channels.get(name='community_projects') - msg = SlackMessage(to=channel) - msg.attachments.append(att) - await slack.send(msg) - - -def pull_request_format(event, data): - footer = '+ {add} / - {del_}'.format( - add=event['pull_request']['additions'], - del_=event['pull_request']['deletions'] - ) - - att = Attachment( - fallback='pull request {}'.format(data['action']), - title='Pull request {action} in <{repo_url}|{name}>:' - ' <{url}|{title}>'.format( - repo_url=event['repository']['html_url'], - url=event['pull_request']['html_url'], - name=event['repository']['name'], - action=data['action'], - title=event['pull_request']['title'], - ), - color=data['color'], - text=event['pull_request']['body'], - author_icon=event['sender']['avatar_url'], - author_name=event['sender']['login'], - author_link=event['sender']['html_url'], - footer=footer - ) - - return att +class GitHubEndpoint: + + def __init__(self, config): + self.config = config + + def add(self, github): + github.add_event('issues', self.issues) + github.add_event('pull_request', self.pull_request) + + async def issues(self, event, facades): + att = None + + if event['action'] == 'opened': + att = self._issue_format(event, 'good') + elif event['action'] == 'closed': + att = self._issue_format(event, 'danger') + + if att: + slack = facades.get('slack') + channel = await slack.channels.get( + name=self.config['github']['channel'] + ) + msg = SlackMessage(to=channel) + msg.attachments.append(att) + await slack.send(msg) + + def _issue_format(self, event, color): + att = Attachment( + fallback='issue {}'.format(event['action']), + color=color, + text=event['issue']['body'], + title='Issue {action} in <{repo_url}|{name}>:' + ' <{url}|{title}>'.format( + repo_url=event['repository']['html_url'], + url=event['issue']['html_url'], + name=event['repository']['name'], + action=event['action'], + title=event['issue']['title'] + ), + author_icon=event['sender']['avatar_url'], + author_name=event['sender']['login'], + author_link=event['sender']['html_url'], + footer=', '.join( + label['name'] for label in event['issue']['labels'] + ) + ) + + return att + + async def pull_request(self, event, facades): + att = None + + if event['action'] == 'opened': + data = {'color': 'good', 'action': 'opened'} + att = self._pull_request_format(event, data) + elif event['action'] == 'closed': + if event['pull_request']['merged']: + data = {'color': '#6f42c1', 'action': 'merged'} + else: + data = {'color': 'danger', 'action': 'closed'} + + att = self._pull_request_format(event, data) + + if att: + slack = facades.get('slack') + channel = await slack.channels.get( + name=self.config['github']['channel'] + ) + msg = SlackMessage(to=channel) + msg.attachments.append(att) + await slack.send(msg) + + def _pull_request_format(self, event, data): + footer = '+ {add} / - {del_}'.format( + add=event['pull_request']['additions'], + del_=event['pull_request']['deletions'] + ) + + att = Attachment( + fallback='pull request {}'.format(data['action']), + title='Pull request {action} in <{repo_url}|{name}>:' + ' <{url}|{title}>'.format( + repo_url=event['repository']['html_url'], + url=event['pull_request']['html_url'], + name=event['repository']['name'], + action=data['action'], + title=event['pull_request']['title'], + ), + color=data['color'], + text=event['pull_request']['body'], + author_icon=event['sender']['avatar_url'], + author_name=event['sender']['login'], + author_link=event['sender']['html_url'], + footer=footer + ) + + return att diff --git a/sirbot/pythondev/scheduler.py b/sirbot/pythondev/scheduler.py index f27ff2b..d80a3f8 100644 --- a/sirbot/pythondev/scheduler.py +++ b/sirbot/pythondev/scheduler.py @@ -4,53 +4,40 @@ logger = logging.getLogger(__name__) -LOOKING = '''*Weekly who's looking thread !* -If you are looking for a job please post your resume in this thread.''' +class SchedulerJobs: -LOOKING_TIPS = '''We are an international community so please include your -location and if you are willing to relocate.''' + def __init__(self, config): + self.config = config -HIRING = '''*Weekly who's hiring thread !* + def add(self, scheduler): + scheduler.add_job('looking_for_job', self.looking_for_job, + trigger='cron', day_of_week=0, hour=8) + scheduler.add_job('hiring', self.hiring, + trigger='cron', day_of_week=0, hour=8) -If you are looking for someone please post your job in this thread.''' + async def looking_for_job(self, facade): + slack = facade.get('slack') -HIRING_TIPS = '''We are an international community so please include your \ -company location and policy on remote employees. A good post should include \ -the name of the company, the location, on-site/remote and a quick summary of \ -the job offer''' + channel = await slack.channels.get(name='python_jobs') + message = SlackMessage(to=channel) + message.text = self.config['python_jobs']['looking'] + await slack.send(message) + message_tips = SlackMessage(to=channel) + message_tips.text = self.config['python_jobs']['looking_tips'] + message_tips.thread = message.thread + await slack.send(message_tips) -def add_to_scheduler(scheduler): - scheduler.add_job('looking_for_job', looking_for_job, - trigger='cron', day_of_week=0, hour=8) - scheduler.add_job('hiring', hiring, - trigger='cron', day_of_week=0, hour=8) + async def hiring(self, facade): + slack = facade.get('slack') + channel = await slack.channels.get(name='python_jobs') + message = SlackMessage(to=channel) + message.text = self.config['python_jobs']['hiring'] + await slack.send(message) -async def looking_for_job(facade): - slack = facade.get('slack') - - channel = await slack.channels.get(name='python_jobs') - message = SlackMessage(to=channel) - message.text = LOOKING - await slack.send(message) - - message_tips = SlackMessage(to=channel) - message_tips.text = LOOKING_TIPS - message_tips.thread = message.thread - await slack.send(message_tips) - - -async def hiring(facade): - slack = facade.get('slack') - - channel = await slack.channels.get(name='python_jobs') - message = SlackMessage(to=channel) - message.text = HIRING - await slack.send(message) - - message_tips = SlackMessage(to=channel) - message_tips.text = HIRING_TIPS - message_tips.thread = message.thread - await slack.send(message_tips) + message_tips = SlackMessage(to=channel) + message_tips.text = self.config['python_jobs']['hiting_tips'] + message_tips.thread = message.thread + await slack.send(message_tips) diff --git a/sirbot/pythondev/slack.py b/sirbot/pythondev/slack.py new file mode 100644 index 0000000..22bd199 --- /dev/null +++ b/sirbot/pythondev/slack.py @@ -0,0 +1,470 @@ +import asyncio +import json +import re + +from sirbot.slack.message import SlackMessage, Attachment, Field, Button, \ + Select + + +class SlackEndpoint: + def __init__(self, config): + self.config = config + + self.config['candy']['user_regex'] = re.compile('<@U.{8}>') + self.config['candy']['trigger_regex'] = re.compile( + self.config['candy']['trigger'] + ) + + def add(self, slack): + slack.add_message('^help', self.help_, flags=re.IGNORECASE, + mention=True) + slack.add_message('tell (<(#|@)(?P[A-Z0-9]*)(|.*)?>)' + ' (?P.*)', + self.publish, flags=re.IGNORECASE, mention=True, + admin=True) + slack.add_message('hello', self.hello, mention=True, + flags=re.IGNORECASE) + + slack.add_command('/admin', func=self.share_admin, public=False) + + slack.add_message(self.config['candy']['trigger'], + self.add_candy_message) + slack.add_command('/leaderboard', self.leaderboard, public=False) + slack.add_event('reaction_added', self.add_candy_reaction) + + slack.add_command('/do', self.share_digital_ocean, public=True) + + slack.add_message('intro doc', + self.intro_doc, mention=True, flags=re.IGNORECASE) + slack.add_message('what to do', + self.what_to_do, mention=True, flags=re.IGNORECASE) + + slack.add_action('choose_file', self.choose_file, public=False) + slack.add_command('/file', self.file, public=False) + + slack.add_command('/gif', self.gif_search, public=False) + slack.add_action('gif_search', self.gif_search_action, public=False) + + slack.add_event('team_join', self.team_join) + + slack.add_command('/pypi', self.pypi_search, public=True) + + async def hello(self, message, slack, *_): + response = message.response() + response.text = 'Hello' + await slack.send(response) + + async def publish(self, message, slack, _, match): + response = message.response() + + to_id = match.group('to_id') + item = match.group('item') + + if to_id.startswith('C'): + to = await slack.channels.get(to_id) + if to: + response.text = item + response.to = to + else: + response.text = 'Sorry I can not understand the destination.' + response.to = response.frm + elif to_id.startswith('U'): + to = await slack.users.get(to_id, dm=True) + if to: + response.text = item + response.to = to + else: + response.text = 'Sorry I can not understand the destination.' + response.to = response.frm + else: + response.text = 'Sorry I can not understand the destination.' + response.to = response.frm + + await slack.send(response) + + async def help_(self, message, slack, *_): + response = message.response() + response.text = 'Help of the good Sir-bot-a-lot' + + help_msg = Attachment(fallback='help', color='good', + title='Common commands') + hello_help = Field(title='Hello', + value='Say hello to Sir-bot-a-lot.' + '\n`@sir-bot-a-lot hello`', + short=True) + admin_help = Field(title='Admin', + value='Send a message to the pythondev admin team.' + '\n `@sir-bot-a-lot admin ...`', + short=True) + intro_doc_help = Field(title='Intro doc', + value='Link the intro doc.' + '\n `@sir-bot-a-lot intro doc`', + short=True) + what_to_do_help = Field(title='What to do', + value='Link the what to do doc.' + '\n `@sir-bot-a-lot what to do`', + short=True) + help_msg.fields.extend( + (hello_help, admin_help, intro_doc_help, what_to_do_help)) + + gif_help = Attachment(fallback='gif help', color='good', + title='Gif commands') + gif_random_help = Field(title='Random', + value='Get a random gif.' + '\n`@sir-bot-a-lot gif`', + short=True) + gif_search_help = Field(title='Search', + value='Search for a gif.' + '\n`@sir-bot-a-lot gif search ...`', + short=True) + gif_trending_help = Field(title='Trending', + value='Get a trending gif.' + '\n`@sir-bot-a-lot gif trending`', + short=True) + gif_by_id_help = Field(title='By ID', + value='''Get a gif by it's id. + \n`@sir-bot-a-lot gif `''', + short=True) + gif_help.fields.extend( + (gif_random_help, gif_search_help, gif_trending_help, + gif_by_id_help)) + + response.attachments.extend((help_msg, gif_help)) + await slack.send(response) + + async def share_admin(self, command, slack, facades): + + response = command.response() + to = await slack.groups.get('G1DRT62UC') + + att = Attachment( + fallback='admin message', + text=command.text, + title='Message to the admin team by <@{}>'.format(command.frm.id), + ) + + response.to = to + response.attachments.append(att) + await slack.send(response) + + async def add_candy_message(self, message, slack, facades, *_): + users_raw = self.config['candy']['user_regex'].findall(message.text) + users = [user[2:-1] for user in users_raw if + user[2:-1] != message.frm.id] + + if not users: + return + + candy = facades.get('candy') + response = message.response() + count = len( + self.config['candy']['trigger_regex'].findall(message.text) + ) + + receivers_messages = list() + for user in users: + slack_user = await slack.users.get(user, dm=True) + user_count = await candy.add(user, count) + msg = response.clone() + msg.to = slack_user + msg.text = '<@{sender}> gave you {count} {trigger}.' \ + ' You now have {user_count} {trigger}.' + msg.text.format( + sender=message.frm.id, + count=count, + trigger=self.config['candy']['trigger'], + user_count=user_count + ) + receivers_messages.append(msg) + + response.to = message.frm + response.text = 'You gave {count} {trigger} to <@{user}>'.format( + count=count, + trigger=self.config['candy']['trigger'], + user=', '.join(users)) + + await slack.send(response) + for msg in receivers_messages: + await slack.send(msg) + + async def add_candy_reaction(self, event, slack, facades): + if event['reaction'] == 'bdfl' and event['user'] != event['item_user']: + candy = facades.get('candy') + user_count = await candy.add(event['item_user']) + + message_from = SlackMessage( + to=(await slack.users.get(event['user'], dm=True))) + message_from.text = 'You gave 1 {trigger} to <@{user}>'.format( + trigger=self.config['candy']['trigger'], + user=event['item_user']) + + message_to = SlackMessage( + to=(await slack.users.get(event['item_user'], dm=True))) + message_to.text = '<@{sender}> gave you 1 {trigger}. ' \ + 'You now have {user_count} {trigger}.' + message_to.txt.format( + sender=event['user'], + trigger=self.config['candy']['trigger'], + user_count=user_count + ) + await slack.send(message_from) + await slack.send(message_to) + + else: + return + + async def leaderboard(self, command, slack, facades): + response = command.response() + + candy = facades.get('candy') + data = await candy.top(count=10) + att = Attachment( + title='{} Leaderboard'.format(self.config['candy']['trigger']), + fallback='{} Leaderboard'.format(self.config['candy']['trigger']), + color='good' + ) + + if not data: + response.text = 'No data to display' + else: + response.response_type = 'in_channel' + for user in data: + slack_user = await slack.users.get(user['user']) + att.text += '{} *{}*\n'.format(slack_user.name, user['candy']) + + response.attachments.append(att) + + await slack.send(response) + + async def share_digital_ocean(self, command, slack, facades): + response = command.response() + response.text = self.config['digital_ocean']['msg'].format( + self.config['digital_ocean']['url'], + self.config['digital_ocean']['refferral'], + ) + await slack.send(response) + + async def file(self, command, slack, *_): + response = command.response() + + if command.text == 'intro': + response.response_type = 'in_channel' + response.text = self.config['files']['intro_doc'] + elif command.text == 'what to do': + response.response_type = 'in_channel' + response.text = self.config['files']['what_to_do'] + else: + att = Attachment( + title='Choose a file to show', + fallback='Choose a file to show', + callback_id='choose_file' + ) + + data = [ + { + 'text': 'Intro doc', + 'value': 'intro_doc' + }, + { + 'text': 'What to do doc', + 'value': 'what_to_do_doc' + } + ] + + select = Select( + name='choose_file', + text='Choose a file', + options=data + ) + att.actions.append(select) + response.attachments.append(att) + + await slack.send(response) + + async def choose_file(self, action, slack, *_): + value = action.action['selected_options'][0]['value'] + response = action.response() + + try: + response.replace_original = False + response.text = self.config['files']['msg'].format( + self.config['files'][value]['url'], + self.config['files'][value]['name'] + ) + except KeyError: + response.text = '''Sorry we could not find this file''' + + await slack.send(response) + + async def intro_doc(self, message, slack, *_): + response = message.response() + response.text = self.config['files']['msg'].format( + self.config['files']['intro_doc']['url'], + self.config['files']['intro_doc']['name'] + ) + await slack.send(response) + + async def what_to_do(self, message, slack, *_): + response = message.response() + response.text = self.config['files']['msg'].format( + self.config['files']['what_to_do']['url'], + self.config['files']['what_to_do']['name'] + ) + await slack.send(response) + + async def gif_search(self, command, slack, facades): + response = command.response() + giphy = facades.get('giphy') + + if command.text: + urls = await giphy.search(command.text) + + att = Attachment( + title='You searched for `{}`'.format(command.text), + fallback='You searched for `{}`'.format(command.text), + image_url=urls[0], + callback_id='gif_search' + ) + + data = json.dumps( + {'urls': urls, 'search': command.text, 'index': 0} + ) + ok = Button(name='ok', text='Send', style='primary', value=data) + next_ = Button(name='next', text='Next', value=data) + + att.actions = [ok, next_] + response.attachments.append(att) + await slack.send(response) + else: + url = giphy.trending() + + att = Attachment( + title='Trending gif on giphy', + fallback='Trending gif on giphy', + image_url=url, + ) + response.attachments.append(att) + await slack.send(response) + + async def gif_search_action(self, action, slack, facades): + response = action.response() + data = json.loads(action.action['value']) + + if action.action['name'] == 'ok': + title = '<@{}> Searched giphy for: `{}`'.format( + action.frm.id, + data['search'] + ) + + att = Attachment( + title=title, + fallback=title, + image_url=data['urls'][data['index']], + ) + + response.attachments.append(att) + response.replace_original = False + await slack.send(response) + + confirm = action.response() + confirm.text = 'Gif successfully sent' + await slack.send(confirm) + + elif action.action['name'] in ('next', 'previous'): + + if action.action['name'] == 'next': + index = data['index'] + 1 + else: + index = data['index'] - 1 + + url = data['urls'][index] + + att = Attachment( + title='Giphy search: `{}`'.format(data['search']), + fallback='Giphy search: `{}`'.format(data['search']), + image_url=url, + callback_id='gif_search' + ) + + data['index'] = index + data_json = json.dumps(data) + + ok = Button(name='ok', text='Send', style='primary', + value=data_json) + att.actions.append(ok) + + if index != 0: + previous = Button( + name='previous', + text='Previous', + value=data_json + ) + att.actions.append(previous) + + if len(data['urls']) > index + 1: + next_ = Button(name='next', text='Next', value=data_json) + att.actions.append(next_) + + response.attachments.append(att) + await slack.send(response) + + else: + return + + async def team_join(self, event, slack, _): + await asyncio.sleep(60) + to = await slack.users.get(event['user']['id'], dm=True) + message = SlackMessage(to=to) + message.text = self.config['join'].format( + self.config['files']['intro_doc']) + await slack.send(message) + + async def pypi_search(self, command, slack, facades): + response = command.response() + pypi = facades.get('pypi') + results = await pypi.search(command.text) + + if not command.text: + response.text = 'Please enter the package name you wish to find' + await slack.send(response) + return + + if results: + for result in results[:3]: + att = Attachment( + title=result['name'], + fallback=result['name'], + text=result['summary'], + title_link='{}/{}'.format(pypi.ROOT_URL, result['name']) + ) + response.attachments.append(att) + + if len(results) == 4: + att = Attachment( + title=results[3]['name'], + fallback=results[3]['name'], + text=results[3]['summary'], + title_link='{}/{}'.format( + pypi.ROOT_URL, results[3]['name'] + ) + ) + response.attachments.append(att) + + elif len(results) > 3: + path = pypi.SEARCH_PATH.format(command.text) + more_info = Attachment( + title='{0} more results..'.format(len(results) - 3), + fallback='{0} more results..'.format(len(results) - 3), + title_link='{}/{}'.format(pypi.ROOT_URL, path) + ) + response.attachments.append(more_info) + + response.response_type = 'in_channel' + response.text = "<@{}> Searched PyPi for `{}`".format( + command.frm.id, + command.text) + else: + response.text = "Could not find anything on PyPi matching" \ + " `{0}`".format(command.text) + + await slack.send(response) diff --git a/sirbot/pythondev/slack/__init__.py b/sirbot/pythondev/slack/__init__.py deleted file mode 100644 index 50210f9..0000000 --- a/sirbot/pythondev/slack/__init__.py +++ /dev/null @@ -1,104 +0,0 @@ -import re - -from sirbot.slack.message import Attachment, Field - -from . import candy, giphy, intro, pypi, files, admin, do - - -def add_to_slack(slack): - slack.add_message('^help', help_, flags=re.IGNORECASE, mention=True) - slack.add_message('tell (<(#|@)(?P[A-Z0-9]*)(|.*)?>) (?P.*)', - publish, flags=re.IGNORECASE, mention=True, admin=True) - slack.add_message('hello', hello, mention=True, flags=re.IGNORECASE) - - candy.add_to_slack(slack) - giphy.add_to_slack(slack) - intro.add_to_slack(slack) - pypi.add_to_slack(slack) - files.add_to_slack(slack) - admin.add_to_slack(slack) - do.add_to_slack(slack) - - -async def hello(message, slack, *_): - response = message.response() - response.text = 'Hello' - await slack.send(response) - - -async def publish(message, slack, _, match): - response = message.response() - - to_id = match.group('to_id') - item = match.group('item') - - if to_id.startswith('C'): - to = await slack.channels.get(to_id) - if to: - response.text = item - response.to = to - else: - response.text = 'Sorry I can not understand the destination.' - response.to = response.frm - elif to_id.startswith('U'): - to = await slack.users.get(to_id, dm=True) - if to: - response.text = item - response.to = to - else: - response.text = 'Sorry I can not understand the destination.' - response.to = response.frm - else: - response.text = 'Sorry I can not understand the destination.' - response.to = response.frm - - await slack.send(response) - - -async def help_(message, slack, *_): - response = message.response() - response.text = 'Help of the good Sir-bot-a-lot' - - help_msg = Attachment(fallback='help', color='good', - title='Common commands') - hello_help = Field(title='Hello', - value='Say hello to Sir-bot-a-lot.' - '\n`@sir-bot-a-lot hello`', - short=True) - admin_help = Field(title='Admin', - value='Send a message to the pythondev admin team.' - '\n `@sir-bot-a-lot admin ...`', - short=True) - intro_doc_help = Field(title='Intro doc', - value='Link the intro doc.' - '\n `@sir-bot-a-lot intro doc`', - short=True) - what_to_do_help = Field(title='What to do', - value='Link the what to do doc.' - '\n `@sir-bot-a-lot what to do`', - short=True) - help_msg.fields.extend( - (hello_help, admin_help, intro_doc_help, what_to_do_help)) - - gif_help = Attachment(fallback='gif help', color='good', - title='Gif commands') - gif_random_help = Field(title='Random', - value='Get a random gif.\n`@sir-bot-a-lot gif`', - short=True) - gif_search_help = Field(title='Search', - value='Search for a gif.' - '\n`@sir-bot-a-lot gif search ...`', - short=True) - gif_trending_help = Field(title='Trending', - value='Get a trending gif.' - '\n`@sir-bot-a-lot gif trending`', - short=True) - gif_by_id_help = Field(title='By ID', - value='''Get a gif by it's id. - \n`@sir-bot-a-lot gif `''', - short=True) - gif_help.fields.extend( - (gif_random_help, gif_search_help, gif_trending_help, gif_by_id_help)) - - response.attachments.extend((help_msg, gif_help)) - await slack.send(response) diff --git a/sirbot/pythondev/slack/admin.py b/sirbot/pythondev/slack/admin.py deleted file mode 100644 index 84bdd91..0000000 --- a/sirbot/pythondev/slack/admin.py +++ /dev/null @@ -1,21 +0,0 @@ -from sirbot.slack.message import Attachment - - -def add_to_slack(slack): - slack.add_command('/admin', func=share_admin, public=False) - - -async def share_admin(command, slack, facades): - - response = command.response() - to = await slack.groups.get('G1DRT62UC') - - att = Attachment( - fallback='admin message', - text=command.text, - title='Message to the admin team by <@{}>'.format(command.frm.id), - ) - - response.to = to - response.attachments.append(att) - await slack.send(response) diff --git a/sirbot/pythondev/slack/candy.py b/sirbot/pythondev/slack/candy.py deleted file mode 100644 index 79b760e..0000000 --- a/sirbot/pythondev/slack/candy.py +++ /dev/null @@ -1,97 +0,0 @@ -import re - -from sirbot.slack.message import Attachment, SlackMessage - -TRIGGER = ':bdfl:' -USER_REGEX = re.compile('<@U.{8}>') -TRIGGER_REGEX = re.compile(TRIGGER) - - -def add_to_slack(slack): - slack.add_message(TRIGGER, add_candy_message) - slack.add_command('/leaderboard', func=leaderboard, public=False) - slack.add_event('reaction_added', add_candy_reaction) - - -async def add_candy_message(message, slack, facades, *_): - users_raw = USER_REGEX.findall(message.text) - users = [user[2:-1] for user in users_raw if - user[2:-1] != message.frm.id] - - if not users: - return - - candy = facades.get('candy') - response = message.response() - count = len(TRIGGER_REGEX.findall(message.text)) - - receivers_messages = list() - for user in users: - slack_user = await slack.users.get(user, dm=True) - user_count = await candy.add(user, count) - msg = response.clone() - msg.to = slack_user - msg.text = '<@{sender}> gave you {count} {trigger}. You now have ' \ - '{user_count} {trigger}'.format(sender=message.frm.id, - count=count, - trigger=TRIGGER, - user_count=user_count) - receivers_messages.append(msg) - - response.to = message.frm - response.text = 'You gave {count} {trigger} to <@{user}>'.format( - count=count, - trigger=TRIGGER, - user=', '.join(users)) - - await slack.send(response) - for msg in receivers_messages: - await slack.send(msg) - - -async def add_candy_reaction(event, slack, facades): - if event['reaction'] == 'bdfl' and event['user'] != event['item_user']: - candy = facades.get('candy') - user_count = await candy.add(event['item_user']) - - message_from = SlackMessage( - to=(await slack.users.get(event['user'], dm=True))) - message_from.text = 'You gave 1 {trigger} to <@{user}>'.format( - trigger=TRIGGER, user=event['item_user']) - - message_to = SlackMessage( - to=(await slack.users.get(event['item_user'], dm=True))) - message_to.text = '<@{sender}> gave you 1 {trigger}. ' \ - 'You now have {user_count} ' \ - '{trigger}'.format(sender=event['user'], - trigger=TRIGGER, - user_count=user_count) - await slack.send(message_from) - await slack.send(message_to) - - else: - return - - -async def leaderboard(command, slack, facades): - response = command.response() - - candy = facades.get('candy') - data = await candy.top(count=10) - att = Attachment( - title='{} Leaderboard'.format(TRIGGER), - fallback='{} Leaderboard'.format(TRIGGER), - color='good' - ) - - if not data: - response.text = 'No data to display' - else: - response.response_type = 'in_channel' - for user in data: - slack_user = await slack.users.get(user['user']) - att.text += '{} *{}*\n'.format(slack_user.name, user['candy']) - - response.attachments.append(att) - - await slack.send(response) diff --git a/sirbot/pythondev/slack/do.py b/sirbot/pythondev/slack/do.py deleted file mode 100644 index d34403d..0000000 --- a/sirbot/pythondev/slack/do.py +++ /dev/null @@ -1,18 +0,0 @@ -DIGITAL_OCEAN_REFFERRAL_URL = 'https://m.do.co/c/457f0988c477' -DIGITAL_OCEAN_URL = 'https://digitalocean.com' - -DIGITAL_OCEAN_TEXT = '''Here at Python Developers we host our website and -Slack bot on <{}|Digital Ocean>. If you are planning on using Digital Ocean, -please use our <{}|referral code>. You get 10 USD, while helping support the - community by contributing to hosting fees for our site and @sirbotalot! - '''.format(DIGITAL_OCEAN_URL, DIGITAL_OCEAN_REFFERRAL_URL) - - -def add_to_slack(slack): - slack.add_command('/do', func=share_digital_ocean, public=True) - - -async def share_digital_ocean(command, slack, facades): - response = command.response() - response.text = DIGITAL_OCEAN_TEXT - await slack.send(response) diff --git a/sirbot/pythondev/slack/files.py b/sirbot/pythondev/slack/files.py deleted file mode 100644 index 9d12365..0000000 --- a/sirbot/pythondev/slack/files.py +++ /dev/null @@ -1,79 +0,0 @@ -import re - -from sirbot.slack.message import Attachment, Select - - -def add_to_slack(slack): - slack.add_message('intro doc', - intro_doc, mention=True, flags=re.IGNORECASE) - slack.add_message('what to do', - what_to_do, mention=True, flags=re.IGNORECASE) - - slack.add_action('choose_file', choose_file, public=False) - slack.add_command('/file', file, public=False) - - -async def file(command, slack, *_): - response = command.response() - - if command.text == 'intro': - response.response_type = 'in_channel' - response.text = 'https://pythondev.slack.com/files/mikefromit/F25EDF4KW/Intro_Doc' # noQa - elif command.text == 'what to do': - response.response_type = 'in_channel' - response.text = 'https://pythondev.slack.com/files/ndevox/F4A137J0J/What_to_do_next_on_your_Python_journey' # noQa - else: - att = Attachment( - title='Choose a file to show', - fallback='Choose a file to show', - callback_id='choose_file' - ) - - data = [ - { - 'text': 'Intro doc', - 'value': 'intro_doc' - }, - { - 'text': 'What to do doc', - 'value': 'what_to_do_doc' - } - ] - - select = Select( - name='choose_file', - text='Choose a file', - options=data - ) - att.actions.append(select) - response.attachments.append(att) - - await slack.send(response) - - -async def choose_file(action, slack, *_): - value = action.action['selected_options'][0]['value'] - response = action.response() - - if value == 'intro_doc': - response.replace_original = False - response.text = 'https://pythondev.slack.com/files/mikefromit/F25EDF4KW/Intro_Doc' # noQa - elif value == 'what_to_do_doc': - response.replace_original = False - response.text = 'https://pythondev.slack.com/files/ndevox/F4A137J0J/What_to_do_next_on_your_Python_journey' # noQa - else: - response.text = '''Sorry we could not find this file''' - - await slack.send(response) - - -async def intro_doc(message, slack, *_): - response = message.response() - response.text = 'https://pythondev.slack.com/files/mikefromit/F25EDF4KW/Intro_Doc' # noqa - await slack.send(response) - - -async def what_to_do(message, slack, *_): - response = message.response() - response.text = 'https://pythondev.slack.com/files/ndevox/F4A137J0J/What_to_do_next_on_your_Python_journey' # noqa - await slack.send(response) diff --git a/sirbot/pythondev/slack/giphy.py b/sirbot/pythondev/slack/giphy.py deleted file mode 100644 index b871818..0000000 --- a/sirbot/pythondev/slack/giphy.py +++ /dev/null @@ -1,106 +0,0 @@ -import json - -from sirbot.slack.message import Attachment, Button - - -def add_to_slack(slack): - slack.add_command('/gif', gif_search, public=False) - slack.add_action('gif_search', gif_search_action, public=False) - - -async def gif_search(command, slack, facades): - response = command.response() - giphy = facades.get('giphy') - - if command.text: - urls = await giphy.search(command.text) - - att = Attachment( - title='You searched for `{}`'.format(command.text), - fallback='You searched for `{}`'.format(command.text), - image_url=urls[0], - callback_id='gif_search' - ) - - data = json.dumps({'urls': urls, 'search': command.text, 'index': 0}) - ok = Button(name='ok', text='Send', style='primary', value=data) - next_ = Button(name='next', text='Next', value=data) - - att.actions = [ok, next_] - response.attachments.append(att) - await slack.send(response) - else: - url = giphy.trending() - - att = Attachment( - title='Trending gif on giphy', - fallback='Trending gif on giphy', - image_url=url, - ) - response.attachments.append(att) - await slack.send(response) - - -async def gif_search_action(action, slack, facades): - response = action.response() - data = json.loads(action.action['value']) - - if action.action['name'] == 'ok': - title = '<@{}> Searched giphy for: `{}`'.format( - action.frm.id, - data['search'] - ) - - att = Attachment( - title=title, - fallback=title, - image_url=data['urls'][data['index']], - ) - - response.attachments.append(att) - response.replace_original = False - await slack.send(response) - - confirm = action.response() - confirm.text = 'Gif successfully sent' - await slack.send(confirm) - - elif action.action['name'] in ('next', 'previous'): - - if action.action['name'] == 'next': - index = data['index'] + 1 - else: - index = data['index'] - 1 - - url = data['urls'][index] - - att = Attachment( - title='Giphy search: `{}`'.format(data['search']), - fallback='Giphy search: `{}`'.format(data['search']), - image_url=url, - callback_id='gif_search' - ) - - data['index'] = index - data_json = json.dumps(data) - - ok = Button(name='ok', text='Send', style='primary', value=data_json) - att.actions.append(ok) - - if index != 0: - previous = Button( - name='previous', - text='Previous', - value=data_json - ) - att.actions.append(previous) - - if len(data['urls']) > index + 1: - next_ = Button(name='next', text='Next', value=data_json) - att.actions.append(next_) - - response.attachments.append(att) - await slack.send(response) - - else: - return diff --git a/sirbot/pythondev/slack/intro.py b/sirbot/pythondev/slack/intro.py deleted file mode 100644 index 0445834..0000000 --- a/sirbot/pythondev/slack/intro.py +++ /dev/null @@ -1,28 +0,0 @@ -import asyncio - -from sirbot.slack.message import SlackMessage - -INTRO_URL = 'https://github.com/pyslackers/community/blob/master/' \ - 'introduction.md' - -INTRO_TEXT = '''Welcome to the community :tada: - - -We are glad that you have decided to join us. We have documented a few things -in the <{}|intro doc> to help you along from the beginning because -we are grand believers in the Don't Repeat Yourself principle, and it just -seems so professional! - -May your :taco:s be plentiful!'''.format(INTRO_URL) - - -def add_to_slack(slack): - slack.add_event('team_join', team_join) - - -async def team_join(event, slack, _): - await asyncio.sleep(60) - to = await slack.users.get(event['user']['id'], dm=True) - message = SlackMessage(to=to) - message.text = INTRO_TEXT - await slack.send(message) diff --git a/sirbot/pythondev/slack/pypi.py b/sirbot/pythondev/slack/pypi.py deleted file mode 100644 index 8d85688..0000000 --- a/sirbot/pythondev/slack/pypi.py +++ /dev/null @@ -1,53 +0,0 @@ -from sirbot.slack.message import Attachment - - -def add_to_slack(slack): - slack.add_command('/pypi', pypi_search, public=True) - - -async def pypi_search(command, slack, facades): - response = command.response() - pypi = facades.get('pypi') - results = await pypi.search(command.text) - - if not command.text: - response.text = 'Please enter the package name you wish to find' - await slack.send(response) - return - - if results: - for result in results[:3]: - att = Attachment( - title=result['name'], - fallback=result['name'], - text=result['summary'], - title_link='{}/{}'.format(pypi.ROOT_URL, result['name']) - ) - response.attachments.append(att) - - if len(results) == 4: - att = Attachment( - title=results[3]['name'], - fallback=results[3]['name'], - text=results[3]['summary'], - title_link='{}/{}'.format(pypi.ROOT_URL, results[3]['name']) - ) - response.attachments.append(att) - - elif len(results) > 3: - path = pypi.SEARCH_PATH.format(command.text) - more_info = Attachment( - title='{0} more results..'.format(len(results) - 3), - fallback='{0} more results..'.format(len(results) - 3), - title_link='{}/{}'.format(pypi.ROOT_URL, path) - ) - response.attachments.append(more_info) - - response.response_type = 'in_channel' - response.text = "<@{}> Searched PyPi for `{}`".format(command.frm.id, - command.text) - else: - response.text = "Could not find anything on PyPi matching" \ - " `{0}`".format(command.text) - - await slack.send(response) diff --git a/test.yml b/test.yml index 5c5ab31..03b15eb 100644 --- a/test.yml +++ b/test.yml @@ -6,23 +6,79 @@ sirbot: - sirbot.plugins.giphy - sirbot.plugins.pypi - sirbot.plugins.candy - - sirbot.plugins.github + - sirbot.plugins.scheduler - sirbot.pythondev +github: + endpoint: "/github" + slack: priority: 20 + rtm: true save: messages: true events: false commands: true actions: true + endpoints: + commands: "/commands" + actions: "/buttons" + events: "/events" sqlite: priority: 80 - file: ':memory:' + file: /opt/sirbot/sirbot.db pythondev: - priority: 1 + priority: 1 + candy: + trigger: ":bdfl:" + githhub: + channel: community_projects + python_jobs: + looking: > + *Weekly who's looking thread !* + + If you are looking for a job please post your resume in this thread. + looking_tips: > + We are an international community so please include your location and + if you are willing to relocate. + hiring: > + *Weekly who's hiring thread !* + + If you are looking for someone please post your job in this thread. + hiring_tips: > + We are an international community so please include your company + location and policy on remote employees. A good post should include the + name of the company, the location, on-site/remote and a quick summary + of the job. + digital_ocean: + url: https://digitalocean.com + refferral: https://m.do.co/c/457f0988c477 + msg: > + Here at Python Developers we host our website and Slack bot on + <{}|Digital Ocean>. If you are planning on using Digital Ocean, please + use our <{}|referral code>. You get 10 USD, while helping support the + community by contributing to hosting fees for our site and + <@sirbotalotovv2>! + files: + msg: > + Click <{}|here> to access the {}. + intro_doc: + name: Intro doc + url: 'https://github.com/pyslackers/community/blob/master/introduction.md' + what_to_do: + name: What to do doc + url: 'https://pythondev.slack.com/files/ndevox/F4A137J0J/What_to_do_next_on_your_Python_journey' + join: > + Welcome to the community :tada: + + We are glad that you have decided to join us. We have documented a few + things in the <{}|intro doc> to help you along from the beginning because + we are grand believers in the Don't Repeat Yourself principle, and it just + seems so professional!\n\n + + May your :taco:s be plentiful! logging: version: 1 @@ -38,8 +94,7 @@ logging: loggers: sirbot: level: DEBUG - handlers: - - console + handlers: [console] propagate: no - sirbot.sqlite: - level: WARNING + sirbot.plugins.sqlite: + level: WARNING \ No newline at end of file