libs.data.modify
lib/data/modify.py
1""" 2lib/data/modify.py 3""" 4 5import logging 6import os 7import re 8import shutil 9from contextlib import closing 10 11import libs.global_value as g 12from cls.score import GameResult 13from cls.timekit import ExtendedDatetime as ExtDt 14from libs.data import lookup 15from libs.functions import message, slack_api 16from libs.utils import dbutil, formatter 17 18 19def db_insert(detection: GameResult, reactions_data: list | None = None) -> None: 20 """スコアデータをDBに追加する 21 22 Args: 23 detection (GameResult): スコアデータ 24 reactions_data (list | None, optional): リアクションリスト. Defaults to None. 25 """ 26 27 detection.calc(ts=g.msg.event_ts) 28 param = { 29 "playtime": ExtDt(float(g.msg.event_ts)).format("sql"), 30 "reactions_data": reactions_data, 31 "rpoint_sum": detection.rpoint_sum(), 32 **detection.to_dict(), 33 } 34 35 if g.msg.updatable: 36 with closing(dbutil.get_connection()) as cur: 37 cur.execute(g.sql["RESULT_INSERT"], param) 38 cur.commit() 39 logging.notice("%s, user=%s", detection, g.msg.user_id) # type: ignore 40 score_reactions(param) 41 else: 42 slack_api.post_message(message.reply(message="restricted_channel"), g.msg.event_ts) 43 44 45def db_update(detection: GameResult, reactions_data: list | None = None) -> None: 46 """スコアデータを変更する 47 48 Args: 49 detection (GameResult): スコアデータ 50 reactions_data (list | None, optional): リアクションリスト. Defaults to None. 51 """ 52 53 detection.calc(ts=g.msg.event_ts) 54 param = { 55 "playtime": ExtDt(float(g.msg.event_ts)).format("sql"), 56 "reactions_data": reactions_data, 57 "rpoint_sum": detection.rpoint_sum(), 58 **detection.to_dict(), 59 } 60 61 if g.msg.updatable: 62 with closing(dbutil.get_connection()) as cur: 63 cur.execute(g.sql["RESULT_UPDATE"], param) 64 cur.commit() 65 logging.notice("%s, user=%s", detection, g.msg.user_id) # type: ignore 66 score_reactions(param) 67 else: 68 slack_api.post_message(message.reply(message="restricted_channel"), g.msg.event_ts) 69 70 71def db_delete(ts: str): 72 """スコアデータを削除する 73 74 Args: 75 ts (str): 削除対象レコードのタイムスタンプ 76 """ 77 78 if g.msg.updatable: 79 with closing(dbutil.get_connection()) as cur: 80 delete_list = cur.execute("select event_ts from remarks where thread_ts=?", (ts,)).fetchall() 81 cur.execute(g.sql["RESULT_DELETE"], (ts,)) 82 delete_result = cur.execute("select changes();").fetchone()[0] 83 cur.execute(g.sql["REMARKS_DELETE_ALL"], (ts,)) 84 delete_remark = cur.execute("select changes();").fetchone()[0] 85 cur.commit() 86 87 if delete_result: 88 logging.notice("result: ts=%s, user=%s, count=%s", ts, g.msg.user_id, delete_result) # type: ignore 89 if delete_remark: 90 logging.notice("remark: ts=%s, user=%s, count=%s", ts, g.msg.user_id, delete_remark) # type: ignore 91 92 # リアクションをすべて外す 93 for icon in lookup.api.reactions_status(): 94 slack_api.call_reactions_remove(icon) 95 # メモのアイコンを外す 96 for x in delete_list: 97 for icon in lookup.api.reactions_status(ts=x): 98 slack_api.call_reactions_remove(icon, ts=x) 99 100 101def db_backup() -> str: 102 """データベースのバックアップ 103 104 Returns: 105 str: 動作結果メッセージ 106 """ 107 108 if not g.cfg.db.backup_dir: # バックアップ設定がされていない場合は何もしない 109 return "" 110 111 fname = os.path.splitext(g.cfg.db.database_file)[0] 112 fext = os.path.splitext(g.cfg.db.database_file)[1] 113 bktime = ExtDt().format("ext") 114 bkfname = os.path.join(g.cfg.db.backup_dir, os.path.basename(f"{fname}_{bktime}{fext}")) 115 116 if not os.path.isdir(g.cfg.db.backup_dir): # バックアップディレクトリ作成 117 try: 118 os.mkdir(g.cfg.db.backup_dir) 119 except OSError as e: 120 logging.error(e, exc_info=True) 121 logging.error("Database backup directory creation failed !!!") 122 return "\nバックアップ用ディレクトリ作成の作成に失敗しました。" 123 124 # バックアップディレクトリにコピー 125 try: 126 shutil.copyfile(g.cfg.db.database_file, bkfname) 127 logging.notice("database backup: %s", bkfname) # type: ignore 128 return "\nデータベースをバックアップしました。" 129 except OSError as e: 130 logging.error(e, exc_info=True) 131 logging.error("Database backup failed !!!") 132 return "\nデータベースのバックアップに失敗しました。" 133 134 135def remarks_append(remarks: dict | list) -> None: 136 """メモをDBに記録する 137 138 Args: 139 remarks (dict | list): メモに残す内容 140 """ 141 142 if isinstance(remarks, dict): 143 remarks = [remarks] 144 145 if g.msg.updatable: 146 with closing(dbutil.get_connection()) as cur: 147 for para in remarks: 148 # 親スレッドの情報 149 row = cur.execute("select * from result where ts=:thread_ts", para).fetchone() 150 if row: 151 if para["name"] in [v for k, v in dict(row).items() if k.endswith("_name")]: 152 cur.execute(g.sql["REMARKS_INSERT"], para) 153 logging.notice("insert: %s, user=%s", para, g.msg.user_id) # type: ignore 154 155 if g.cfg.setting.reaction_ok not in lookup.api.reactions_status(ts=para.get("event_ts")): 156 slack_api.call_reactions_add(g.cfg.setting.reaction_ok, ts=para.get("event_ts")) 157 158 cur.commit() 159 160 161def remarks_delete(ts: str) -> None: 162 """DBからメモを削除する 163 164 Args: 165 ts (str): 削除対象レコードのタイムスタンプ 166 """ 167 168 if g.msg.updatable: 169 with closing(dbutil.get_connection()) as cur: 170 cur.execute(g.sql["REMARKS_DELETE_ONE"], (ts,)) 171 count = cur.execute("select changes();").fetchone()[0] 172 cur.commit() 173 174 if count: 175 logging.notice("ts=%s, user=%s, count=%s", ts, g.msg.user_id, count) # type: ignore 176 177 if g.msg.status != "message_deleted": 178 if g.cfg.setting.reaction_ok in lookup.api.reactions_status(): 179 slack_api.call_reactions_remove(g.cfg.setting.reaction_ok, ts=ts) 180 181 182def remarks_delete_compar(para: dict) -> None: 183 """DBからメモを削除する(突合) 184 185 Args: 186 para (dict): パラメータ 187 """ 188 189 ch: str | None 190 191 with closing(dbutil.get_connection()) as cur: 192 cur.execute(g.sql["REMARKS_DELETE_COMPAR"], para) 193 cur.commit() 194 195 left = cur.execute("select count() from remarks where event_ts=:event_ts;", para).fetchone()[0] 196 197 if g.msg.channel_id: 198 ch = g.msg.channel_id 199 else: 200 ch = lookup.api.get_channel_id() 201 202 icon = lookup.api.reactions_status(ts=para.get("event_ts")) 203 if g.cfg.setting.reaction_ok in icon and left == 0: 204 slack_api.call_reactions_remove(g.cfg.setting.reaction_ok, ch=ch, ts=para.get("event_ts")) 205 206 207def check_remarks() -> None: 208 """メモの内容を拾ってDBに格納する""" 209 game_result = lookup.db.exsist_record(g.msg.thread_ts) 210 if game_result.has_valid_data(): # ゲーム結果のスレッドになっているか 211 g.cfg.results.initialization() 212 g.cfg.results.unregistered_replace = False # ゲスト無効 213 214 remarks: list = [] 215 for name, matter in zip(g.msg.argument[0::2], g.msg.argument[1::2]): 216 remark = { 217 "thread_ts": g.msg.thread_ts, 218 "event_ts": g.msg.event_ts, 219 "name": formatter.name_replace(name), 220 "matter": matter, 221 } 222 if remark["name"] in game_result.to_list() and remark not in remarks: 223 remarks.append(remark) 224 225 match g.msg.status: 226 case "message_append": 227 remarks_append(remarks) 228 case "message_changed": 229 remarks_delete(g.msg.event_ts) 230 remarks_append(remarks) 231 case "message_deleted": 232 remarks_delete(g.msg.event_ts) 233 234 235def reprocessing_remarks(): 236 """スレッドの内容を再処理""" 237 res = lookup.api.get_conversations() 238 msg = res.get("messages") 239 240 if msg: 241 reply_count = msg[0].get("reply_count", 0) 242 g.msg.thread_ts = msg[0].get("ts") 243 244 for x in range(1, reply_count + 1): 245 g.msg.event_ts = msg[x].get("ts") 246 text = str(msg[x].get("text", "")) 247 logging.info("(%s/%s) thread_ts=%s, event_ts=%s, %s", x, reply_count, g.msg.thread_ts, g.msg.event_ts, text) 248 249 if text: 250 g.msg.keyword = text.split()[0] 251 g.msg.argument = text.split()[1:] 252 253 if re.match(rf"^{g.cfg.cw.remarks_word}", g.msg.keyword): 254 check_remarks() 255 256 257def score_reactions(param: dict): 258 """素点合計をチェックしリアクションを付ける 259 260 Args: 261 param (dict): 素点データ 262 """ 263 264 correct_score = g.cfg.mahjong.origin_point * 4 # 配給原点 265 rpoint_sum = param["rpoint_sum"] # 素点合計 266 267 if param["reactions_data"]: 268 icon = param["reactions_data"] 269 else: 270 icon = lookup.api.reactions_status() 271 272 if rpoint_sum == correct_score: 273 if g.cfg.setting.reaction_ng in icon: 274 slack_api.call_reactions_remove(g.cfg.setting.reaction_ng) 275 if g.cfg.setting.reaction_ok not in icon: 276 slack_api.call_reactions_add(g.cfg.setting.reaction_ok) 277 else: 278 if g.cfg.setting.reaction_ok in icon: 279 slack_api.call_reactions_remove(g.cfg.setting.reaction_ok) 280 if g.cfg.setting.reaction_ng not in icon: 281 slack_api.call_reactions_add(g.cfg.setting.reaction_ng) 282 283 slack_api.post_message( 284 message.reply(message="invalid_score", rpoint_sum=rpoint_sum), 285 g.msg.event_ts, 286 )
20def db_insert(detection: GameResult, reactions_data: list | None = None) -> None: 21 """スコアデータをDBに追加する 22 23 Args: 24 detection (GameResult): スコアデータ 25 reactions_data (list | None, optional): リアクションリスト. Defaults to None. 26 """ 27 28 detection.calc(ts=g.msg.event_ts) 29 param = { 30 "playtime": ExtDt(float(g.msg.event_ts)).format("sql"), 31 "reactions_data": reactions_data, 32 "rpoint_sum": detection.rpoint_sum(), 33 **detection.to_dict(), 34 } 35 36 if g.msg.updatable: 37 with closing(dbutil.get_connection()) as cur: 38 cur.execute(g.sql["RESULT_INSERT"], param) 39 cur.commit() 40 logging.notice("%s, user=%s", detection, g.msg.user_id) # type: ignore 41 score_reactions(param) 42 else: 43 slack_api.post_message(message.reply(message="restricted_channel"), g.msg.event_ts)
スコアデータをDBに追加する
Arguments:
- detection (GameResult): スコアデータ
- reactions_data (list | None, optional): リアクションリスト. Defaults to None.
46def db_update(detection: GameResult, reactions_data: list | None = None) -> None: 47 """スコアデータを変更する 48 49 Args: 50 detection (GameResult): スコアデータ 51 reactions_data (list | None, optional): リアクションリスト. Defaults to None. 52 """ 53 54 detection.calc(ts=g.msg.event_ts) 55 param = { 56 "playtime": ExtDt(float(g.msg.event_ts)).format("sql"), 57 "reactions_data": reactions_data, 58 "rpoint_sum": detection.rpoint_sum(), 59 **detection.to_dict(), 60 } 61 62 if g.msg.updatable: 63 with closing(dbutil.get_connection()) as cur: 64 cur.execute(g.sql["RESULT_UPDATE"], param) 65 cur.commit() 66 logging.notice("%s, user=%s", detection, g.msg.user_id) # type: ignore 67 score_reactions(param) 68 else: 69 slack_api.post_message(message.reply(message="restricted_channel"), g.msg.event_ts)
スコアデータを変更する
Arguments:
- detection (GameResult): スコアデータ
- reactions_data (list | None, optional): リアクションリスト. Defaults to None.
def
db_delete(ts: str):
72def db_delete(ts: str): 73 """スコアデータを削除する 74 75 Args: 76 ts (str): 削除対象レコードのタイムスタンプ 77 """ 78 79 if g.msg.updatable: 80 with closing(dbutil.get_connection()) as cur: 81 delete_list = cur.execute("select event_ts from remarks where thread_ts=?", (ts,)).fetchall() 82 cur.execute(g.sql["RESULT_DELETE"], (ts,)) 83 delete_result = cur.execute("select changes();").fetchone()[0] 84 cur.execute(g.sql["REMARKS_DELETE_ALL"], (ts,)) 85 delete_remark = cur.execute("select changes();").fetchone()[0] 86 cur.commit() 87 88 if delete_result: 89 logging.notice("result: ts=%s, user=%s, count=%s", ts, g.msg.user_id, delete_result) # type: ignore 90 if delete_remark: 91 logging.notice("remark: ts=%s, user=%s, count=%s", ts, g.msg.user_id, delete_remark) # type: ignore 92 93 # リアクションをすべて外す 94 for icon in lookup.api.reactions_status(): 95 slack_api.call_reactions_remove(icon) 96 # メモのアイコンを外す 97 for x in delete_list: 98 for icon in lookup.api.reactions_status(ts=x): 99 slack_api.call_reactions_remove(icon, ts=x)
スコアデータを削除する
Arguments:
- ts (str): 削除対象レコードのタイムスタンプ
def
db_backup() -> str:
102def db_backup() -> str: 103 """データベースのバックアップ 104 105 Returns: 106 str: 動作結果メッセージ 107 """ 108 109 if not g.cfg.db.backup_dir: # バックアップ設定がされていない場合は何もしない 110 return "" 111 112 fname = os.path.splitext(g.cfg.db.database_file)[0] 113 fext = os.path.splitext(g.cfg.db.database_file)[1] 114 bktime = ExtDt().format("ext") 115 bkfname = os.path.join(g.cfg.db.backup_dir, os.path.basename(f"{fname}_{bktime}{fext}")) 116 117 if not os.path.isdir(g.cfg.db.backup_dir): # バックアップディレクトリ作成 118 try: 119 os.mkdir(g.cfg.db.backup_dir) 120 except OSError as e: 121 logging.error(e, exc_info=True) 122 logging.error("Database backup directory creation failed !!!") 123 return "\nバックアップ用ディレクトリ作成の作成に失敗しました。" 124 125 # バックアップディレクトリにコピー 126 try: 127 shutil.copyfile(g.cfg.db.database_file, bkfname) 128 logging.notice("database backup: %s", bkfname) # type: ignore 129 return "\nデータベースをバックアップしました。" 130 except OSError as e: 131 logging.error(e, exc_info=True) 132 logging.error("Database backup failed !!!") 133 return "\nデータベースのバックアップに失敗しました。"
データベースのバックアップ
Returns:
str: 動作結果メッセージ
def
remarks_append(remarks: dict | list) -> None:
136def remarks_append(remarks: dict | list) -> None: 137 """メモをDBに記録する 138 139 Args: 140 remarks (dict | list): メモに残す内容 141 """ 142 143 if isinstance(remarks, dict): 144 remarks = [remarks] 145 146 if g.msg.updatable: 147 with closing(dbutil.get_connection()) as cur: 148 for para in remarks: 149 # 親スレッドの情報 150 row = cur.execute("select * from result where ts=:thread_ts", para).fetchone() 151 if row: 152 if para["name"] in [v for k, v in dict(row).items() if k.endswith("_name")]: 153 cur.execute(g.sql["REMARKS_INSERT"], para) 154 logging.notice("insert: %s, user=%s", para, g.msg.user_id) # type: ignore 155 156 if g.cfg.setting.reaction_ok not in lookup.api.reactions_status(ts=para.get("event_ts")): 157 slack_api.call_reactions_add(g.cfg.setting.reaction_ok, ts=para.get("event_ts")) 158 159 cur.commit()
メモをDBに記録する
Arguments:
- remarks (dict | list): メモに残す内容
def
remarks_delete(ts: str) -> None:
162def remarks_delete(ts: str) -> None: 163 """DBからメモを削除する 164 165 Args: 166 ts (str): 削除対象レコードのタイムスタンプ 167 """ 168 169 if g.msg.updatable: 170 with closing(dbutil.get_connection()) as cur: 171 cur.execute(g.sql["REMARKS_DELETE_ONE"], (ts,)) 172 count = cur.execute("select changes();").fetchone()[0] 173 cur.commit() 174 175 if count: 176 logging.notice("ts=%s, user=%s, count=%s", ts, g.msg.user_id, count) # type: ignore 177 178 if g.msg.status != "message_deleted": 179 if g.cfg.setting.reaction_ok in lookup.api.reactions_status(): 180 slack_api.call_reactions_remove(g.cfg.setting.reaction_ok, ts=ts)
DBからメモを削除する
Arguments:
- ts (str): 削除対象レコードのタイムスタンプ
def
remarks_delete_compar(para: dict) -> None:
183def remarks_delete_compar(para: dict) -> None: 184 """DBからメモを削除する(突合) 185 186 Args: 187 para (dict): パラメータ 188 """ 189 190 ch: str | None 191 192 with closing(dbutil.get_connection()) as cur: 193 cur.execute(g.sql["REMARKS_DELETE_COMPAR"], para) 194 cur.commit() 195 196 left = cur.execute("select count() from remarks where event_ts=:event_ts;", para).fetchone()[0] 197 198 if g.msg.channel_id: 199 ch = g.msg.channel_id 200 else: 201 ch = lookup.api.get_channel_id() 202 203 icon = lookup.api.reactions_status(ts=para.get("event_ts")) 204 if g.cfg.setting.reaction_ok in icon and left == 0: 205 slack_api.call_reactions_remove(g.cfg.setting.reaction_ok, ch=ch, ts=para.get("event_ts"))
DBからメモを削除する(突合)
Arguments:
- para (dict): パラメータ
def
check_remarks() -> None:
208def check_remarks() -> None: 209 """メモの内容を拾ってDBに格納する""" 210 game_result = lookup.db.exsist_record(g.msg.thread_ts) 211 if game_result.has_valid_data(): # ゲーム結果のスレッドになっているか 212 g.cfg.results.initialization() 213 g.cfg.results.unregistered_replace = False # ゲスト無効 214 215 remarks: list = [] 216 for name, matter in zip(g.msg.argument[0::2], g.msg.argument[1::2]): 217 remark = { 218 "thread_ts": g.msg.thread_ts, 219 "event_ts": g.msg.event_ts, 220 "name": formatter.name_replace(name), 221 "matter": matter, 222 } 223 if remark["name"] in game_result.to_list() and remark not in remarks: 224 remarks.append(remark) 225 226 match g.msg.status: 227 case "message_append": 228 remarks_append(remarks) 229 case "message_changed": 230 remarks_delete(g.msg.event_ts) 231 remarks_append(remarks) 232 case "message_deleted": 233 remarks_delete(g.msg.event_ts)
メモの内容を拾ってDBに格納する
def
reprocessing_remarks():
236def reprocessing_remarks(): 237 """スレッドの内容を再処理""" 238 res = lookup.api.get_conversations() 239 msg = res.get("messages") 240 241 if msg: 242 reply_count = msg[0].get("reply_count", 0) 243 g.msg.thread_ts = msg[0].get("ts") 244 245 for x in range(1, reply_count + 1): 246 g.msg.event_ts = msg[x].get("ts") 247 text = str(msg[x].get("text", "")) 248 logging.info("(%s/%s) thread_ts=%s, event_ts=%s, %s", x, reply_count, g.msg.thread_ts, g.msg.event_ts, text) 249 250 if text: 251 g.msg.keyword = text.split()[0] 252 g.msg.argument = text.split()[1:] 253 254 if re.match(rf"^{g.cfg.cw.remarks_word}", g.msg.keyword): 255 check_remarks()
スレッドの内容を再処理
def
score_reactions(param: dict):
258def score_reactions(param: dict): 259 """素点合計をチェックしリアクションを付ける 260 261 Args: 262 param (dict): 素点データ 263 """ 264 265 correct_score = g.cfg.mahjong.origin_point * 4 # 配給原点 266 rpoint_sum = param["rpoint_sum"] # 素点合計 267 268 if param["reactions_data"]: 269 icon = param["reactions_data"] 270 else: 271 icon = lookup.api.reactions_status() 272 273 if rpoint_sum == correct_score: 274 if g.cfg.setting.reaction_ng in icon: 275 slack_api.call_reactions_remove(g.cfg.setting.reaction_ng) 276 if g.cfg.setting.reaction_ok not in icon: 277 slack_api.call_reactions_add(g.cfg.setting.reaction_ok) 278 else: 279 if g.cfg.setting.reaction_ok in icon: 280 slack_api.call_reactions_remove(g.cfg.setting.reaction_ok) 281 if g.cfg.setting.reaction_ng not in icon: 282 slack_api.call_reactions_add(g.cfg.setting.reaction_ng) 283 284 slack_api.post_message( 285 message.reply(message="invalid_score", rpoint_sum=rpoint_sum), 286 g.msg.event_ts, 287 )
素点合計をチェックしリアクションを付ける
Arguments:
- param (dict): 素点データ