diff --git a/hwe/sammo/API/Message/GetRecentMessage.php b/hwe/sammo/API/Message/GetRecentMessage.php new file mode 100644 index 00000000..ca037df9 --- /dev/null +++ b/hwe/sammo/API/Message/GetRecentMessage.php @@ -0,0 +1,152 @@ +args); + $v->rule('int', 'sequence'); + if (!$v->validate()) { + return $v->errorStr(); + } + $this->args['sequence'] = (int)($this->args['sequence'] ?? -1); + return null; + } + + public function getRequiredSessionMode(): int + { + return static::REQ_GAME_LOGIN; + } + + private function delayFrequentCall(Session $session): void + { + $lastMsgGet = Json::decode($session->lastMsgGet) ?? []; + $now = new \DateTimeImmutable(); + $delayTime = false; + + if (count($lastMsgGet) >= 10) { + try { + if (!is_string($lastMsgGet[0])) { + throw new \Exception('Why not string?'); + } + $first = new \DateTimeImmutable($lastMsgGet[0]); + $diffSec = TimeUtil::DateIntervalToSeconds($first->diff($now)); + if ($diffSec < 1) { + $delayTime = true; + } + array_shift($lastMsgGet); + } catch (\Exception $e) { + $lastMsgGet = []; + } + } + $lastMsgGet[] = TimeUtil::format($now, true); + $session->lastMsgGet = Json::encode($lastMsgGet); + + if ($delayTime) { + usleep(200); + } + } + + public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) + { + $this->delayFrequentCall($session); + $session->setReadOnly(); + $userID = $session->getUserID(); + + + + $db = DB::db(); + $me = $db->queryFirstRow('SELECT `no`,`name`,`nation`,`officer_level`,`con`,`picture`,`imgsvr`,penalty,permission FROM general WHERE `owner`=%i', $userID); + + if ($me === null) { + return '장수가 사망했습니다.'; + } + + [$generalID, $nationID, $generalName] = [$me['no'], $me['nation'], $me['name']]; + $permission = checkSecretPermission($me, false); + $reqSequence = $this->args['sequence']; + + $result = []; + $result['result'] = true; + $result['keepRecent'] = false; + $nextSequence = $reqSequence; + $minSequence = $reqSequence; + $lastType = null; + + $result['private'] = array_map(function (Message $msg) use (&$nextSequence, &$minSequence, &$lastType) { + if ($msg->id > $nextSequence) { + $nextSequence = $msg->id; + } + if ($msg->id <= $minSequence) { + $minSequence = $msg->id; + $lastType = 'private'; + } + return $msg->toArray(); + }, Message::getMessagesFromMailBox($generalID, MessageType::private, 15, $reqSequence)); + + $result['public'] = array_map(function (Message $msg) use (&$nextSequence, &$minSequence, &$lastType) { + if ($msg->id > $nextSequence) { + $nextSequence = $msg->id; + } + if ($msg->id <= $minSequence) { + $minSequence = $msg->id; + $lastType = 'public'; + } + return $msg->toArray(); + }, Message::getMessagesFromMailBox(Message::MAILBOX_PUBLIC, MessageType::public, 15, $reqSequence)); + + $result['national'] = array_map(function (Message $msg) use (&$nextSequence, &$minSequence, &$lastType) { + if ($msg->id > $nextSequence) { + $nextSequence = $msg->id; + } + if ($msg->id <= $minSequence) { + $minSequence = $msg->id; + $lastType = 'national'; + } + return $msg->toArray(); + }, Message::getMessagesFromMailBox(Message::MAILBOX_NATIONAL + $nationID, MessageType::national, 15, $reqSequence)); + + $result['diplomacy'] = array_map(function (Message $msg) use (&$nextSequence, &$minSequence, &$lastType, $permission) { + if ($msg->id > $nextSequence) { + $nextSequence = $msg->id; + } + if ($msg->id <= $minSequence) { + $minSequence = $msg->id; + $lastType = 'diplomacy'; + } + $values = $msg->toArray(); + if ($msg->dest->nationID != 0 && $permission < 3) { + $values['text'] = '(외교 메시지입니다)'; //TODO: 외교서신이라 읽을 수 없음을 보여줘야함 + $values['option']['invalid'] = true; + } + return $values; + }, Message::getMessagesFromMailBox(Message::MAILBOX_NATIONAL + $nationID, MessageType::diplomacy, 15, $reqSequence)); + + if ($lastType !== null) { + array_pop($result[$lastType]); + $result['keepRecent'] = true; + } else if ($reqSequence <= 0) { + $result['keepRecent'] = true; + } + + $result['sequence'] = $nextSequence; + $result['nationID'] = $nationID; + $result['generalName'] = $generalName; + + return $result; + } +} diff --git a/hwe/ts/SammoAPI.ts b/hwe/ts/SammoAPI.ts index a61bbea8..189523bd 100644 --- a/hwe/ts/SammoAPI.ts +++ b/hwe/ts/SammoAPI.ts @@ -32,6 +32,7 @@ GetRecentRecordResponse, import type { CachedMapResult, GeneralListResponse, ItemTypeKey, MapResult } from "./defs"; import type { VoteDetailResult, VoteListResult } from "./defs/API/Vote"; import type { ActiveResourceAuctionList, OpenAuctionResponse, UniqueItemAuctionDetail, UniqueItemAuctionList } from "./defs/API/Auction"; +import type { MsgResponse } from "./defs/API/Message"; const apiRealPath = { Auction: { @@ -170,6 +171,11 @@ const apiRealPath = { lastID: number }, InheritLogResponse> }, + Message: { + GetRecentMessage: GET as APICallT<{ + sequence?: number; + }, MsgResponse>, + }, Misc: { UploadImage: POST as APICallT< { diff --git a/hwe/ts/defs/API/Message.ts b/hwe/ts/defs/API/Message.ts new file mode 100644 index 00000000..b7c91efa --- /dev/null +++ b/hwe/ts/defs/API/Message.ts @@ -0,0 +1,54 @@ +import type { ValidResponse } from "@/util/callSammoAPI"; + +export type MsgType = "private" | "public" | "national" | "diplomacy"; + +export const minMsgSeq: Record = { + private: 0x7fffffff, + public: 0x7fffffff, + national: 0x7fffffff, + diplomacy: 0x7fffffff, +}; + +export type MsgTarget = { + id: number; + name: string; + nation_id: number; //XXX: 왜 이 값은 nationID가 아니고 nation_id인가? + nation: string; + color: string; + icon: string; +}; + +export type MsgItem = { + id: number; + msgType: MsgType; + src: MsgTarget; + dest?: MsgTarget; + text: string; + option: Record; + time: string; +}; + +export type MsgPrintItem = MsgItem & { + generalName: string; + nationID: number; + nationType: "local" | "src" | "dest"; + myGeneralID: number; + allowButton: boolean; + last5min: string; + now: string; + invalidType: "msg_invalid" | "msg_valid"; + deletable: boolean; + src: MsgTarget & { colorType: "bright" | "dark" }; + dest: MsgTarget & { colorType: "bright" | "dark" }; + defaultIcon: string; +}; + +export type MsgResponse = { + [v in MsgType]: MsgItem[]; +} & { + result: true; + keepRecent: boolean; + nationID: number; + generalName: string; + sequence: number; +}; \ No newline at end of file