def remove_account(credentials, talk_id): # アカウント連携を解除 try: credentials.revoke(httplib2.Http()) except client.TokenRevokeError: print('既にアカウント連携が削除されています') # DBから削除 session = Session() with session.begin(): talk = session.query(Talk).filter_by(talk_id=talk_id).one() session.delete(talk)
def oauth2callback(): print(flask.session) session = Session() if 'talk_id' not in flask.session: return '不正なアクセスです。' talk_id = flask.session.pop('talk_id') auth_code = flask.request.args.get('code') credentials = flow.step2_exchange(auth_code) with session.begin(): if session.query(Talk).filter( Talk.talk_id == talk_id).one_or_none() is None: session.add( Talk(talk_id=talk_id, credential=credentials.to_json())) return 'あなたのLineとGoogleカレンダーが正常に紐付けられました。' else: return '既にグループにGoogleアカウントが紐付けられています'
def handle(event): print("postbackevent: {}".format(event)) session = Session() data = event.postback.data.split(',') print(data) print(data[1]) pre_time = datetime.strptime(data[-1], '%Y-%m-%d %H:%M:%S') compare = datetime.now() - pre_time print(compare) if compare.total_seconds() > int(line_env['time_out_seconds']): line_bot_api.reply_message( event.reply_token, TextSendMessage(text="タイムアウトです。\nもう一度最初からやり直してください")) return talk_id = self._get_talk_id(event) credentials = api_manager.get_credentials(talk_id) if credentials == REFRESH_ERROR: reply_refresh_error_message(event) return for func, template_id in self.preexe_cases: if data[0] == template_id: func(event, data, credentials) return service = api_manager.build_service(credentials) if service is None: reply_google_auth_message(event) return for func, template_id in self.cases: if data[0] == template_id: func(event, data, credentials, service) return
def get_credentials(talk_id): session = Session() with session.begin(): talk = session.query(Talk).filter_by(talk_id=talk_id).one_or_none() if talk is None: return None credentials = client.OAuth2Credentials.from_json(talk.credential) if credentials.access_token_expired: print('認証の期限が切れています') http = credentials.authorize(httplib2.Http()) try: credentials.refresh(http) except client.HttpAccessTokenRefreshError: print('リフレッシュエラーが起きました') session.delete(talk) print('ユーザーをDBから削除しました') return REFRESH_ERROR print('リフレッシュしました') talk.credential = credentials.to_json() print('新しい認証情報をDBに保存しました') return credentials
def __init__(self, handler): self.handler = handler self.cases = [] self.preexe_cases = [] self.session = Session() @self._add_case(template_id='ExitConfirm_yes', preexe=True) def exit_confirm(event, data, credentials): try: talk_id = self._get_talk_id(event) line_bot_api.reply_message( event.reply_token, StickerSendMessage(package_id="2", sticker_id="42")) if credentials is not None: api_manager.remove_account(credentials, talk_id) if event.source.type == 'group': line_bot_api.leave_group(event.source.group_id) else: line_bot_api.leave_room(event.source.room_id) except LineBotApiError as e: print(e) @self._add_case(template_id='ExitConfirm_no', preexe=True) def exit_confirm_no(event, data, credentials): line_bot_api.reply_message(event.reply_token, TextSendMessage(text="退出をキャンセルしました。")) @self._add_case(template_id='AccountRemoveConfirm_yes') def account_remove(event, data, credentials, _): talk_id = self._get_talk_id(event) api_manager.remove_account(credentials, talk_id) line_bot_api.reply_message(event.reply_token, TextSendMessage(text='アカウント連携を解除しました。')) @self._add_case(template_id='AccountRemoveConfirm_no') def account_remove_no(event, data, credentials, service): line_bot_api.reply_message( event.reply_token, TextSendMessage(text="アカウント連携解除をキャンセルしました。")) @self._add_case(template_id='EventCreateButtons_#create-calendar') def calendar_create(event, data, credentials, service): talk_id = self._get_talk_id(event) created_datetime = datetime.strptime(data[1], '%m/%d') current_year = datetime.now(jst).year created_date = date( current_year if is_over_now(created_datetime) else current_year + 1, created_datetime.month, created_datetime.day) title = 'Smart Scheduleからの予定' with self.session.begin(): talk = self.session.query(Talk).filter_by( talk_id=talk_id).one() try: calendar_event = api_manager.create_event( service, talk.calendar_id, created_date, title) except client.HttpAccessTokenRefreshError: with self.session.begin(): self.session.delete(talk) reply_invalid_credential_error_message(event) return reply_text = '{}月{}日の予定を作成しました\n{}'.format( created_date.month, created_date.day, calendar_event.get('htmlLink')) line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self._add_case(template_id='GroupMenuButtons_#member') def member_display(event, data, credentials, service): talk_id = self._get_talk_id(event) reply_text = '登録されているメンバー一覧' with self.session.begin(): talk = self.session.query(Talk).filter_by( talk_id=talk_id).one() user_names = sorted( {free_day.user_name for free_day in talk.free_days}) for user_name in user_names: reply_text += '\n' reply_text += user_name line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self._add_case(template_id='GroupMenuButtons_#adjust') def schedule_adjust(event, data, credentials, service): talk_id = self._get_talk_id(event) with self.session.begin(): talk = self.session.query(Talk).filter_by( talk_id=talk_id).one() reply_text = "空いてる日を入力してください\n例:橋本 1/1 1/2 1/3 1/4\n\n※日程調整を終了する際は「end」と入力してください\n--------------------------------" if len(talk.free_days) == 0: reply_text += '\n\n現在、空いている日は登録されていません' else: reply_text += '\n\n現在の空いている日\n' dates = [free_day.date for free_day in talk.free_days] date_count_dict = OrderedDict( sorted(Counter(dates).items(), key=lambda x: x[0])) for d, count in date_count_dict.items(): reply_text += '\n{}/{} {}票'.format(d.month, d.day, count) line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self._add_case(template_id='GroupMenuButtons_#g-calender') def schedule_display(event, data, credentials, service): post_carousel(event.reply_token) @self._add_case(template_id='#keyword_search') def keyword_search(event, data, credentials, service): talk_id = self._get_talk_id(event) with self.session.begin(): talk = self.session.query(Talk).filter_by( talk_id=talk_id).one() talk.keyword_flag = True line_bot_api.reply_message( event.reply_token, TextSendMessage(text="キーワードを入力してください\n例:バイト、研究室")) @self._add_case(template_id='#up to n days_schedule') def up_to_n_days_schedule(event, data, credentials, service): talk_id = self._get_talk_id(event) with self.session.begin(): talk = self.session.query(Talk).filter_by( talk_id=talk_id).one() talk.up_to_day_flag = True line_bot_api.reply_message( event.reply_token, TextSendMessage(text="何日後までの予定を表示しますか?\n例:5")) @self._add_case(template_id='#date_schedule') def date_schedule(event, data, credentials, service): talk_id = self._get_talk_id(event) with self.session.begin(): talk = self.session.query(Talk).filter_by( talk_id=talk_id).one() talk.date_flag = True line_bot_api.reply_message( event.reply_token, TextSendMessage(text="取得したい予定の日付を入力してください\n例:4/1")) @self._add_case(template_id='#today_schedule') def today_schedule(event, data, credentials, service): talk_id = self._get_talk_id(event) with self.session.begin(): talk = self.session.query(Talk).filter_by( talk_id=talk_id).one() days = 0 try: events = api_manager.get_events_after_n_days( service, talk.calendar_id, days) except client.HttpAccessTokenRefreshError: with self.session.begin(): self.session.delete(talk) reply_invalid_credential_error_message(event) return reply_text = '今日の予定' reply_text = generate_message_from_events(events, reply_text) line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self._add_case(template_id='#tomorrow_schedule') def tomorrow_schedule(event, data, credentials, service): talk_id = self._get_talk_id(event) with self.session.begin(): talk = self.session.query(Talk).filter_by( talk_id=talk_id).one() days = 1 try: events = api_manager.get_events_after_n_days( service, talk.calendar_id, days) except client.HttpAccessTokenRefreshError: with self.session.begin(): self.session.delete(talk) reply_invalid_credential_error_message(event) return reply_text = '明日の予定' reply_text = generate_message_from_events(events, reply_text) line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self._add_case(template_id='#7days_schedule') def seven_days_schedule(event, data, credentials, service): talk_id = self._get_talk_id(event) with self.session.begin(): talk = self.session.query(Talk).filter_by( talk_id=talk_id).one() days = 7 try: events = api_manager.get_n_days_events(service, talk.calendar_id, days) except client.HttpAccessTokenRefreshError: with self.session.begin(): self.session.delete(talk) reply_invalid_credential_error_message(event) return reply_text = '1週間後までの予定' reply_text = generate_message_from_events(events, reply_text) line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self.handler.add(PostbackEvent) def handle(event): print("postbackevent: {}".format(event)) session = Session() data = event.postback.data.split(',') print(data) print(data[1]) pre_time = datetime.strptime(data[-1], '%Y-%m-%d %H:%M:%S') compare = datetime.now() - pre_time print(compare) if compare.total_seconds() > int(line_env['time_out_seconds']): line_bot_api.reply_message( event.reply_token, TextSendMessage(text="タイムアウトです。\nもう一度最初からやり直してください")) return talk_id = self._get_talk_id(event) credentials = api_manager.get_credentials(talk_id) if credentials == REFRESH_ERROR: reply_refresh_error_message(event) return for func, template_id in self.preexe_cases: if data[0] == template_id: func(event, data, credentials) return service = api_manager.build_service(credentials) if service is None: reply_google_auth_message(event) return for func, template_id in self.cases: if data[0] == template_id: func(event, data, credentials, service) return
def __init__(self, handler): self.handler = handler self.cases = [] self.preexe_cases = [] self.flag_cases = [] self.session = Session() @self._add_case(text='exit', type=['group', 'room'], preexe=True) def exit(event): time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') confirm_message = TemplateSendMessage( alt_text='Confirm template', template=ExitConfirm(time, messages['templates']['exit_confirm'])) line_bot_api.reply_message(event.reply_token, confirm_message) @self._add_case(text='help', type='user', preexe=True) def user_help(event): reply_text = self.message_event_messages['user_help_message'] line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self._add_case(text='help', type=['group', 'room'], preexe=True) def group_help(event): reply_text = self.message_event_messages['group_help_message'] line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self._add_case(text=['schedule', '予定']) def schedule(event, service): post_carousel(event.reply_token) @self._add_case(type=['group', 'room'], pattern=r'(ss|smart[\s_-]?schedule|スマートスケジュール)$', pattern_flags=re.IGNORECASE) def group_menu(event, service): time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') # グループのメニューを表示する buttons_template_message = TemplateSendMessage( alt_text='Button template', template=GroupMenuButtons( time, messages['templates']['group_menu_buttons'])) line_bot_api.reply_message(event.reply_token, buttons_template_message) @self._add_case(text='select') def select(event, service): talk_id = self._get_talk_id(event) with self.session.begin(): talk = self.session.query(Talk).filter( Talk.talk_id == talk_id).one() talk.calendar_select_flag = True try: calendar_list = api_manager.get_calendar_list(service) except client.HttpAccessTokenRefreshError: self.session.delete(talk) reply_invalid_credential_error_message(event) return reply_text = 'Google Calendar で確認できるカレンダーの一覧です。\n 文字を入力してカレンダーを選択してください' for item in calendar_list['items']: reply_text += '\n- {}'.format(item['summary']) line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self._add_case(text='logout') def logout(event, service): time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') confirm_message = TemplateSendMessage( alt_text='Confirm template', template=AccountRemoveConfirm( time, messages['templates']['account_remove_confirm'])) line_bot_api.reply_message(event.reply_token, confirm_message) @self._add_case( pattern= r'[^\s\d]\S*(?:\s(?:[1-9]|1[0-2])/(?:[1-9]|[1-2][0-9]|3[0-1]))+$') def add_free_day(event, service): talk_id = self._get_talk_id(event) with self.session.begin(): talk = self.session.query(Talk).filter( Talk.talk_id == talk_id).one() split_message = event.message.text.split() name = split_message[0] day_strs = split_message[1:] datetimes = [ datetime.strptime(day_str, '%m/%d') for day_str in day_strs ] # TODO 無効な日にち(31を指定したが、その月の31日が存在しない場合など)のエラーハンドリングやメッセージを実装する(ValueError) current_year = datetime.now(jst).year dates = [ date(current_year if is_over_now(dt) else current_year + 1, dt.month, dt.day) for dt in datetimes ] free_days = [FreeDay(date, name, talk.id) for date in dates] with self.session.begin(): # TODO 既にDBにある日を登録しようとしたときのエラーハンドリング及びメッセージの実装(sqlalchemy.exc.IntegrityError) self.session.add_all(free_days) line_bot_api.reply_message(event.reply_token, TextSendMessage(text='空いている日を保存しました')) @self._add_case(text='end') def end(event, service): talk_id = self._get_talk_id(event) with self.session.begin(): talk = self.session.query(Talk).filter( Talk.talk_id == talk_id).one() if len(talk.free_days) == 0: # TODO 登録されている空いている日は無いことを知らせるメッセージが欲しい print('空いている日が登録されてないぞー') return time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') reply_text = '空いている日' dates = [free_day.date for free_day in talk.free_days] date_count_dict = OrderedDict( sorted(Counter(dates).items(), key=lambda x: x[0])) for i, dic in enumerate(date_count_dict.items()): d, count = dic if i % 3 == 0: reply_text += '\n' else: reply_text += ', ' reply_text += '{}/{} {}人'.format(d.month, d.day, count) best_date_count = max(date_count_dict.values()) best_dates = [ k for k, v in date_count_dict.items() if v == best_date_count ] print(len(reply_text)) if len(reply_text) >= 100: reply_text = reply_text[:100] buttons_template_message = TemplateSendMessage( alt_text='Button template', # TODO ボタンテンプレートが4つしか受け付けないので4つしか選べない template=EventCreateButtons( time, messages['templates']['event_create_buttons'], reply_text, best_dates[:4])) line_bot_api.reply_message(event.reply_token, buttons_template_message) with self.session.begin(): for free_day in talk.free_days: self.session.delete(free_day) @self._add_flag_case(flag='up_to_day_flag') def up_to_day(event, service, talk): talk.up_to_day_flag = False try: days = int(event.message.text) except ValueError: # TODO 数字ではないメッセージが送られてきたときのメッセージを送る return try: events = api_manager.get_n_days_events(service, talk.calendar_id, days) except client.HttpAccessTokenRefreshError: self.session.delete(talk) reply_invalid_credential_error_message(event) return reply_text = '{}日後までの予定'.format(days) reply_text = generate_message_from_events(events, reply_text) line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self._add_flag_case(flag='date_flag') def day_by_date(event, service, talk): talk.date_flag = False try: current_year = datetime.now(jst).year dt = datetime.strptime(event.message.text, '%m/%d') specified_date = date( current_year if is_over_now(dt) else current_year + 1, dt.month, dt.day) except ValueError: # TODO 不適当なメッセージが送られてきたときのメッセージを送る return try: events = api_manager.get_events_by_date( service, talk.calendar_id, specified_date) except client.HttpAccessTokenRefreshError: self.session.delete(talk) reply_invalid_credential_error_message(event) return reply_text = '{} の予定'.format(event.message.text) reply_text = generate_message_from_events(events, reply_text) line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self._add_flag_case(flag='keyword_flag') def keyword(event, service, talk): talk.keyword_flag = False keyword = event.message.text try: events = api_manager.get_events_by_title( service, talk.calendar_id, keyword) except client.HttpAccessTokenRefreshError: self.session.delete(talk) reply_invalid_credential_error_message(event) return reply_text = '{}の検索結果'.format(keyword) reply_text = generate_message_from_events(events, reply_text) if len(reply_text) >= 1900: reply_text = reply_text[:1900] line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self._add_flag_case(flag='calendar_select_flag') def calendar_select(event, service, talk): try: calendar_list = api_manager.get_calendar_list(service) except client.HttpAccessTokenRefreshError: self.session.delete(talk) reply_invalid_credential_error_message(event) return summaries = [item['summary'] for item in calendar_list['items']] if event.message.text in summaries: talk.calendar_select_flag = False calendar_id = [ item['id'] for item in calendar_list['items'] if item['summary'] == event.message.text ][0] talk.calendar_id = calendar_id reply_text = 'カレンダーを {} に設定しました'.format(event.message.text) line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) else: talk.calendar_select_flag = False reply_text = '{} はカレンダーには存在しません'.format(event.message.text) line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_text)) @self.handler.add(MessageEvent, message=TextMessage) def handle(event): print(event) for func, text, type, pattern, pattern_flags in self.preexe_cases: if self._validate(event, text, type, pattern, pattern_flags): func(event) return service = self._get_service(event) # リフレッシュエラーが起きた場合、手動でアカウント連携を解除するように促すメッセージを送る if service == REFRESH_ERROR: reply_refresh_error_message(event) return # DBに登録されていない場合、認証URLをリプライする if service is None: reply_google_auth_message(event) return for func, flag in self.flag_cases: talk_id = self._get_talk_id(event) # TODO トランザクション開始とコミットのタイミングこれじゃまずい self.session.begin() talk = self.session.query(Talk).filter( Talk.talk_id == talk_id).one() try: if getattr(talk, flag): func(event, service, talk) self.session.commit() return finally: self.session.close() for func, text, type, pattern, pattern_flags in self.cases: if self._validate(event, text, type, pattern, pattern_flags): func(event, service) return