def app(message: Message): global DRIVER text = message.body["text"] argv = shlex.split(text.split("$app", 1)[1], posix=True) output_list = [] with contextlib.redirect_stdout(StringIO()) as o: with contextlib.redirect_stderr(o): try: if DRIVER is None: parser = Parser(prog="$app") DRIVER = runtime.Driver(parser=parser) DRIVER.run(argv, module=sys.modules[__name__], debug=True) else: DRIVER._run(argv, debug=True) except Exit as e: logger.debug("exit: %r", e) output = o.getvalue() if output.strip(): output_list.append(output) new_line = "\n" if output_list: message.send(f"""\ ``` {new_line.join(output_list).strip(new_line)} ```""")
def hello_send(message: Message): import shlex text = message.body["text"] parser = Parser(prog="$hello") Injector(hello).inject(parser) argv = shlex.split(text.split("$hello", 1)[1], posix=True) try: args = parser.parse_args(argv) except Exit as e: texts = [] if e.args[0]: texts.append(e.args[0]) texts.append(parser.format_help()) new_line = "\n" message.send(f"""\ ``` {new_line.join(texts).strip(new_line)} ```""") return params = vars(args).copy() with contextlib.redirect_stdout(StringIO()) as o: hello(**params) output = o.getvalue() if output.strip(): message.send(output)
def describe_handler(message: Message, command, entry_no): logging.info(message.body) if entry_no is None or entry_no == '': message.send(f"{command} のあとに登録番号(半角数字のみ)を入力してください") return Describe().specified_entry_no(message, entry_no)
def __validate_book_price(self, book_price: str, message: Message) -> bool: """ 立替金額のバリデーション :param book_price: 立替金額 :return: """ if not book_price.isdecimal(): message.send("立替金額は数字で入力してください", thread_ts=message.body['ts']) return False return True
def hello(msg: Message, number_of_die: str, side_of_die: str): # roll_result = [ random.randrange(1, int(side_of_die), 1) for i in range(int(number_of_die)) ] # 주사위를 던진 횟수만큼 나온 숫자를 모두 더합니다. roll_sum = sum(roll_result) # 주사위를 던져서 나온 숫자와 합을 메시지로 출력합니다. msg.send(str(roll_result)) msg.send(str(roll_sum))
def botsend(message: Message, text: str) -> None: """ スレッドの親かどうかで応答先を切り替える message.send() の代わりの関数 :param messsage: slackbotのmessageオブジェクト :param text: 送信するテキストメッセージ """ if "thread_ts" in message.body: # スレッド内のメッセージの場合 message.send(text, thread_ts=message.thread_ts) else: # 親メッセージの場合 message.send(text, thread_ts=None)
def hello(msg: Message, number_of_die: str, side_of_die: str): # 'roll 던지는횟수d숫자면체' 로 # '던지는횟수'를 number_of_die # '숫자면체'를 side_of_die # ' roll_result = [ random.randrange(1, int(side_of_die), 1) for i in range(int(number_of_die)) ] roll_sum = sum(roll_result) msg.send(str(roll_result)) msg.send(str(roll_sum))
def delete_handler(message: Message, command, entry_no): logging.info(message.body) if message.body['channel'] != settings.DEFAULT_CHANNEL_ID: slack = Slack() text = f"公式チャンネル #{slack.get_channel_name(message, channel_id=settings.DEFAULT_CHANNEL_ID)} で実行してください!" slack.send_message_with_link_names(message, message.body['channel'], text) return if entry_no is None or entry_no == '': message.send(f"{command} のあとに登録番号(半角数字のみ)を入力してください") return Delete().specified_entry_no(message, entry_no)
def plusplus_delete(message: Message, subcommand: str, name: str) -> None: """ 指定された名前を削除する カウントが10未満のもののみ削除する """ try: plus = Plusplus.get(name=name) except Plusplus.DoesNotExist: message.send(f"`{name}` という名前は登録されていません") return if abs(plus.counter) > 10: botsend(message, f"`{name}` のカウントが多いので削除を取り消しました(count: {plus.counter})") return plus.delete_instance() message.send(f"`{name}` を削除しました")
def plusplus_rename(message: Message, old: str, new: str) -> None: """ 指定された old から new に名前を変更する """ try: oldplus = Plusplus.get(name=old) except Plusplus.DoesNotExist: botsend(message, f"`{old}` という名前は登録されていません") return newplus, created = Plusplus.get_or_create(name=new, counter=oldplus.counter) if not created: # すでに存在している message.send(f"`{new}` という名前はすでに登録されています") return # 入れ替える oldplus.delete_instance() botsend(message, f"`{old}` から `{new}` に名前を変更しました(count: {oldplus.counter})")
def hello_send(message: Message): import shlex text = message.body["text"] parser = Parser(prog="$hello") parser.add_argument("--name", default="world") argv = shlex.split(text.split("$hello", 1)[1], posix=True) try: args = parser.parse_args(argv) print(f"parsed: {args}", file=sys.stderr) message.send(f"hello {args.name}") except Exit as e: texts = [] if e.args[0]: texts.append(e.args[0]) texts.append(parser.format_help()) new_line = "\n" message.send(f"""\ ``` {new_line.join(texts).strip(new_line)} ```""")
def hello_send(message: Message): import re text = message.body["text"] parser = Parser() parser.add_argument("--name", default="world") argv = [x for x in re.split(r"\s+", text.split("$hello", 1)[1]) if x] try: args = parser.parse_args(argv) print(f"parsed: {args}", file=sys.stderr) return message.send(f"hello {args.name}") except Exit as e: texts = [] if e.args[0]: texts.append(e.args[0]) texts.append(parser.format_help()) new_line = "\n" return message.send( f"""\ ``` {new_line.join(texts)} ```""" )
def late_count(message_info: Message): message = message_info.body['text'] if message.find('이번달') != -1: target_date = date.today() else: try: target_date = parse(message, fuzzy=True).date() except ValueError: target_date = date.today() - relativedelta(months=1) slacker = Slacker(settings.API_TOKEN) channel_id = message_info.channel._body['id'] response = slacker.groups.history(channel_id, count=1000) messages = response.body['messages'] late_info = get_late_count_dict(channel_id, messages, target_date) if message.find('상세') != -1: message = make_late_count_detail_message(target_date, late_info) else: message = make_late_count_message(target_date, late_info) message_info.send(message)
def save(self, message: Message): now = datetime.now() entry_no = '' impression = '' for block in message.body['blocks']: if not ('text' in block and 'text' in block['text']): continue text = block['text']['text'] if '*登録番号*' in text: entry_no = re.sub('\\D', '', self.converter.to_hankaku(text)) self.logger.debug(f"entry_no={entry_no}") elif '*感想*' in text: impression = re.sub('\*感想\*\n', '', text).strip() self.logger.debug(f"impression={impression}") # 投稿タイムスタンプ(スレッド投稿のため) ts = message.body['ts'] # パラメータチェック if not self.validation.validate_impression(entry_no=entry_no, message=message): self.logger.debug("パラメータチェックNG") return # 申請者 Slack ID slack_id = self.slack.get_slack_id_from_workflow(message.body['text']) # 更新対象レコードを取得 items = self.dynamodb.query_specified_key_value(self.dynamodb.default_table, 'entry_no', entry_no) # プライマリキー指定なので必ず1件取得 item = items[0] if item['slack_id'] != slack_id: message.send(f"<@{slack_id}> 購入者本人以外は感想登録できません!", thread_ts=ts) return False # 感想登録日 impression_time = now.strftime("%Y%m%d%H%M%S") self.logger.debug(f"impression_time={impression_time}") # 感想登録フラグ 1:登録あり impression_flag = '1' # 感想登録 response = self.dynamodb.update_bookbot_entry_impression(entry_no, impression, impression_time, impression_flag) self.logger.debug(response) if response is None: self.logger.error(f"感想の登録に失敗しました") message.send(f"<@{slack_id}> 感想の登録に失敗しました...すいません!", thread_ts=ts) return False item['impression'] = impression reply_texts = [f"<!here> 以下の感想が登録されました!"] reply_texts.append(self.converter.get_list_str(item)) message.send("\n".join(reply_texts), thread_ts=ts)
def hello(msg: Message): msg.send("world!!")
def hello(msg: Message): msg.send("World!")
def __validate_entry_no(self, entry_no: str, message: Message) -> bool: if not entry_no.isdecimal(): message.send("登録番号は数字で入力してください", thread_ts=message.body['ts']) return False return True
def save(self, message: Message): now = datetime.now() book_name = '' book_type = '' book_price = '' book_url = '' purpose = '' for block in message.body['blocks']: if not ('text' in block and 'text' in block['text']): continue text = block['text']['text'] if '*題名*' in text: book_name = re.sub('\*題名\*|\n', '', text) self.logger.debug(f"book_name={book_name}") elif '*形式*' in text: book_type = re.sub('\*形式\*|\n', '', text) self.logger.debug(f"book_type={book_type}") elif '*立替金額(税込)*' in text: book_price = re.sub('\\D', '', self.converter.to_hankaku(text)) self.logger.debug(f"book_price={book_price}") elif '*詳細リンク(Amazonなど)*' in text: book_url = re.sub('\*詳細リンク(Amazonなど)\*|<|>|\n', '', text) self.logger.debug(f"book_url={book_url}") elif '*購入目的*' in text: purpose = re.sub('\*購入目的\*|<|>|\n', '', text) self.logger.debug(f"purpose={purpose}") # 投稿タイムスタンプ(スレッド投稿のため) ts = message.body['ts'] # パラメータチェック if not self.validation.validate_entry(book_name=book_name, book_price=book_price, book_url=book_url, message=message): self.logger.debug("パラメータチェックNG") return # 申請日 entry_time = now.strftime("%Y%m%d%H%M%S") # 申請者 Slack ID slack_id = self.slack.get_slack_id_from_workflow(message.body['text']) # 申請者名 user_name = self.slack.get_user_name(slack_id, message) slack_name = user_name[0] real_name = user_name[1] # 今年度の合計立替金額を取得 total_price_in_this_year = self.amount.get_total_price_in_this_year(slack_name) # そのユーザが年間上限金額以上立替していないか result = self.amount.check_max_amount(slack_name, book_price, total_price_in_this_year) # 今回申請分を登録する前の 残り金額 remain_amount = self.amount.get_remain_amount(slack_name, total_price_in_this_year) if not result: self.logger.debug("年間立替金額上限を超えています") message.send(f"<@{slack_id}> 年間立替金額上限を超えるため登録できません。残り *{remain_amount}* 円 までならOKです。", thread_ts=ts) return # 受付番号 # アトミックカウンタでシーケンス番号を発行し文字列として取得する entry_no = str(self.dynamodb.atomic_counter('atomic-counter', 'entry_no')) # 今年度の合計立替金額に今回申請分を足す total_price_in_this_year += int(book_price) # 今回申請分を登録した後の 残り金額 remain_amount = self.amount.get_remain_amount(slack_name, total_price_in_this_year) # パーマリンクを取得 permalink = self.slack.get_message_permalink(message) # 感想(空で登録) impression = '' impression_flag = '0' # 0:登録無し item = { 'entry_no': entry_no, 'book_name': book_name, 'book_type': book_type, 'book_price': book_price, 'total_price_in_this_year': total_price_in_this_year, 'remain_amount': remain_amount, 'book_url': book_url, 'purpose': purpose, 'entry_time': entry_time, 'slack_id': slack_id, 'slack_name': slack_name, 'real_name': real_name, 'permalink': permalink, 'impression': impression, 'impression_flag': impression_flag } self.logger.info(f"item={item}") self.dynamodb.insert(self.dynamodb.default_table, item) reply_texts = [f"<@{slack_id}> 登録しました!"] reply_texts.append(f"登録番号: *[{entry_no}]*") reply_texts.append(f"今年度の立替金額合計が *{total_price_in_this_year}* 円になりました。") if remain_amount == 0: reply_texts.append(f"上限金額は *{self.amount.max_amount}* 円です。今年度はこれが最後の立替になります。") else: reply_texts.append(f"残り *{remain_amount}* 円 までならOKです。") if book_price == '0': self.logger.info("0円のため承認PDFは作成しません") message.send("\n".join(reply_texts), thread_ts=ts) return reply_texts.append('補助対象になりますので承認PDFを作成します。') message.send("\n".join(reply_texts), thread_ts=ts) # 承認内容のPDFを作成してSlackにアップ バックアップでS3にアップ # 保存先PDFファイルパス save_pdf_fname = f"{entry_no}_{entry_time}_{slack_name}.pdf" save_pdf_path = f"{app_home}/output_pdf/{save_pdf_fname}" # 承認PDF作成 if not self.pdf.make_approved_pdf(item, save_pdf_path): self.logger.warning('承認PDFの作成に失敗しました') message.send(f"<@{item['slack_id']}> 承認PDFの作成に失敗しました...すいません!") return False channel_name = self.slack.get_channel_name(message) self.logger.debug(channel_name) self.slack.upload_file(message, channel_name, fpath=save_pdf_path, comment="このPDFと購入時の領収書を添付して立替金申請をしてください。", thread_ts=ts) # S3にもアップロード this_year = self.converter.get_this_year_from_today()[0][0:4] # 今年度のYYYY self.s3.upload_to_pdf(save_pdf_path, process_yyyy=this_year)