package main import ( "regexp" "sync" "time" "github.com/streadway/amqp" tb "gopkg.in/tucnak/telebot.v2" ) const maxUnixTimestamp int64 = 2147483647 const chtwrsbotID64 int64 = 408101137 const angrybirbsbotID64 int64 = 833409972 type DataBackup struct { Messages []ChatWarsMessage `json:"messages"` } type ObjType struct { ID64 int64 IntlId string `json:"intl_id"` Name string `json:"name"` } type ObjSubType struct { ID64 int64 IntlId string `json:"intl_id"` Name string `json:"name"` ObjType string `json:"obj_type"` ObjTypeID64 int64 } type MQClient struct { User string Password string Host string Path string SSL bool Connection *amqp.Connection Channel *amqp.Channel Queue amqp.Queue } type ChirpClient struct { HeartBeat time.Time `json:"heart_beat"` Login string `json:"nickname"` Active bool TGUserID64 int64 `json:"tg_user_id"` MQ MQClient `json:"mq_client"` CWUserID64 int64 `json:"user_id"` CWGuildID64 int64 `json:"guild_id"` CWRole string `json:"role"` CWState string `json:"state"` CWBusyUntil time.Time `json:"busy_until"` CWLastUpdate time.Time `json:"last_update"` CWIdle bool `json:"idle"` Mux sync.Mutex } type MQKeepAlive struct { TGUserID64 int64 `json:"tg_user_id"` Nickname string `json:"nick"` Queue string `json:"queue"` Date time.Time `json:"date"` } type TGCommand struct { Type int64 `json:"type"` FromChatID64 int64 `json:"from_chat_id"` FromUserID64 int64 `json:"from_user_id"` FromMsgID64 int64 `json:"from_msg_id"` ToChatID64 int64 `json:"to_chat_id"` ToUserID64 int64 `json:"to_user_id"` Text string `json:"text"` Document tb.Document `json:"document"` ParseMode int64 `json:"parse_mode"` Delay time.Duration `json:"delay"` } type ChatWarsCastle struct { ObjID64 int64 `json:"obj_id"` Logo string `json:"logo"` Name string `json:"name"` } type ChatWarsGuild struct { ObjID64 int64 `json:"obj_id"` Tag string `json:"tag"` Name string `json:"name"` } type ChatWarsUser struct { ObjID64 int64 `json:"obj_id"` Name string `json:"name"` } type ChatWarsItem struct { ObjID64 int64 `json:"obj_id"` ItemTypeID int64 `json:"item_type_id"` Code string `json:"code"` Name string `json:"name"` Weight int64 `json:"weight"` Exchange bool `json:"exchange"` Auction bool `json:"auction"` Craft ChatWarsItemCraft `json:"craft"` } type ChatWarsItemCraft struct { Command string `json:"cmd"` Mana int64 `json:"mana"` Items []ChatWarsItems `json:"items"` } type ChatWarsItems struct { Item string `json:"item"` ItemID64 int64 Quantity int64 `json:"quantity"` } type ChatWarsCallback struct { Name string `json:"name"` Data []byte `json:"data"` } type ChatWarsMessage struct { ObjID64 int64 `json:"obj_id"` TGUserID64 int64 `json:"tg_user_id"` TGSenderUserID64 int64 `json:"tg_sender_user_id"` Date time.Time `json:"date"` ID64 int64 `json:"id"` ChatID64 int64 `json:"chat_id"` Text string `json:"text"` IsForwarded bool `json:"is_forwarded"` Callbacks []ChatWarsCallback `json:"callbacks"` } type ChatWarsExchangeDeal struct { ItemID64 int64 `json:"item_id"` Quantity int64 `json:"quantity"` Price int64 `json:"price"` Status string `json:"status"` Link string `json:"link"` } type ChatWarsMessageExchangeAck struct { Msg *ChatWarsMessage `json:"msg"` ActiveDeals int64 `json:"active_deals"` MaxDeals int64 `json:"max_deals"` DealList []ChatWarsExchangeDeal `json:"deals"` } type ChatWarsMessageGStock struct { Msg *ChatWarsMessage `json:"msg"` OwnerID64 int64 `json:"owner_id"` Stock []ChatWarsItems `json:"stock"` } type ChatWarsMessageWithdrawRcv struct { Msg *ChatWarsMessage `json:"msg"` ItemList []ChatWarsItems `json:"item_list"` } type ChatWarsMessageGDepositReq struct { Msg *ChatWarsMessage `json:"msg"` ItemID64 int64 `json:"item_id"` Quantity int64 `json:"quantity"` } type ChatWarsMessageGDepositAck struct { Msg *ChatWarsMessage `json:"msg"` ItemID64 int64 `json:"item_id"` Quantity int64 `json:"quantity"` } type ChatWarsMessageReportAck struct { ObjID64 int64 `json:"obj_id"` } type ChatWarsMessageStockAck struct { Msg *ChatWarsMessage `json:"msg"` Used int64 `json:"used"` Available int64 `json:"available"` Stock []ChatWarsItems `json:"stock"` } type ChatWarsMessageStockAnyAck struct { Msg *ChatWarsMessage `json:"msg"` Stock []ChatWarsItems `json:"stock"` } type ChatWarsMessageOrderbookAck struct { Msg *ChatWarsMessage `json:"msg"` Name string `json:"name"` Code string `json:"code"` Qty1 int64 `json:"qty1"` Price1 int64 `json:"price1"` Qty2 int64 `json:"qty2"` Price2 int64 `json:"price2"` Qty3 int64 `json:"qty3"` Price3 int64 `json:"price3"` Qty int64 `json:"qty"` Gold int64 `json:"gold"` } type ChatWarsMessageMeAck struct { Msg *ChatWarsMessage `json:"msg"` CWUserID64 int64 `json:"user_id"` CWGuildID64 int64 `json:"guild_id"` State string `json:"state"` Level int64 `json:"level"` ExpNow int64 `json:"exp_now"` ExpLvl int64 `json:"exp_lvl"` } type ChatWarsMessageGRolesAck struct { Msg *ChatWarsMessage `json:"msg"` BartenderID64 int64 `json:"bartender"` CommanderID64 int64 `json:"commander"` SquireID64 int64 `json:"squire"` TreasurerID64 int64 `json:"treasurer"` } type ChatWarsMessageGoQuestAck struct { Msg *ChatWarsMessage `json:"msg"` QuestTypeID int `json:"quest"` Duration time.Duration `json:"duration"` } type ChatWarsMessageQuestResult struct { Msg *ChatWarsMessage `json:"msg"` Gold int64 `json:"gold"` Exp int64 `json:"exp"` Loot []ChatWarsItems `json:"loot"` RegenStamina bool `json:"regen_stamina"` LostTorch bool `json:"lost_torch"` } type ChatWarsMessageQuestResultAmbush struct { Msg *ChatWarsMessage `json:"msg"` Loot []ChatWarsItems `json:"loot"` Level int64 `json:"level"` Armored bool `json:"armored"` Enraged bool `json:"enraged"` SpearResists bool `json:"spear resists"` Toughness bool `json:"toughness"` PoisonBottles bool `json:"poison_bottles"` } type ChatWarsMessageDuelFight struct { ObjID64 int64 `json:"obj_id"` WinCastle string `json:"win_castle"` WinGuild string `json:"win_guild"` WinUser string `json:"win_user"` WinLife int64 `json:"win_life"` LossCastle string `json:"loss_castle"` LossGuild string `json:"loss_guild"` LossUser string `json:"loss_user"` LossLife int64 `json:"loss_life"` Exp int64 `json:"exp"` Weapon string `json:"weapon"` } type ChatWarsMessageAuctionAnnounce struct { ObjID64 int64 `json:"obj_id"` LotID int32 `json:"lot_id"` ItemID64 int64 `json:"item_id"` Cond string `json:"cond"` Quality string `json:"quality"` SellerUserID64 int64 `json:"seller_id"` SellerGuildID64 int64 `json:"seller_guild_id"` SellerCastleID64 int64 `json:"seller_castle_id"` BuyerUserID64 int64 `json:"buyer_id"` BuyerGuildID64 int64 `json:"buyer_guild_id"` BuyerCastleID64 int64 `json:"buyer_castle_id"` Price int32 `json:"price"` Status string `json:"status"` End time.Time `json:"end"` } type ChatWarsMessagePillageInc struct { ObjID64 int64 `json:"obj_id"` Attacker string `json:"attacker"` Guild string `json:"guild"` Castle string `json:"castle"` } type ChatWarsMessageUnionWar struct { } type ChatWarsMessageTimeAck struct { RealTime time.Time `json:"real_time"` TimeOfDay string `json:"time_of_day"` Hour int64 `json:"hour"` Minute int64 `json:"minute"` Day int64 `json:"day"` Month int64 `json:"month"` Year int64 `json:"year"` Weather string `json:"weather"` WeatherNext string `json:"weather_next"` } type MessageParsingRule struct { ID int32 Priority int32 `json:"prio"` Description string `json:"descn"` Rule string `json:"rule"` MsgType string `json:"msg_type"` MsgTypeID64 int64 ChatID64 int64 SenderUserID64 int64 re *regexp.Regexp } type BotMsg struct { To tb.Recipient Text string } type Job struct { ID64 int64 JobTypeID int32 Trigger int64 Timeout time.Time UserID64 int64 Payload []byte } type JobPayloadGetHammerTime struct { } type JobPayloadSetDef struct { } type JobPayloadFwdMsg struct { ChatID64 int64 `json:"chat_id"` } type JobPayloadMsgRefresh struct { ObjID64 int64 `json:"obj_id"` } type JobPayloadPillage struct { ObjID64 int64 `json:"obj_id"` Date time.Time `json:"date"` } type JobPayloadTribute struct { } type JobPayloadStatus struct { } type JobPayloadGStock struct { MsgID64 int64 `json:"msg_id"` ChatID64 int64 `json:"chat_id"` Progress int64 `json:"progress"` Stock []ChatWarsItems `json:"stock"` } type JobPayloadGWithdraw struct { MsgID64 int64 `json:"msg_id"` ChatID64 int64 `json:"chat_id"` Request string `json:"request"` Status int `json:"status"` } type JobPayloadGDeposit struct { MsgID64 int64 `json:"msg_id"` ChatID64 int64 `json:"chat_id"` ResObjID64 []int64 `json:"res_obj_id"` Status int `json:"status"` } type JobPayloadGDepositForward struct { ItemID64 int64 `json:"item_id"` Quantity int64 `json:"quantity"` } type JobPayloadSaveRes struct { MsgID64 int64 `json:"msg_id"` ChatID64 int64 `json:"chat_id"` ResObjID64 int64 `json:"res_obj_id"` BuyRes bool `json:"buy_res"` } type JobPayloadRescanMsg struct { Query string `json:"query"` MsgID64 int64 `json:"msg_id"` ChatID64 int64 `json:"chat_id"` } type JobPayloadSetDone struct { JobID64 int64 `json:"job_id"` MsgID64 int64 `json:"msg_id"` ChatID64 int64 `json:"chat_id"` Text string `json:"test"` } type JobPayloadMsgClient struct { Text string `json:"msg"` MsgID64 int64 `json:"msg_id"` ChatID64 int64 `json:"chat_id"` } type JobPayloadBackupExport struct { MsgID64 int64 `json:"msg_id"` ChatID64 int64 `json:"chat_id"` } type JobPayloadBackupImport struct { URL string `json:"url"` MsgID64 int64 `json:"msg_id"` ChatID64 int64 `json:"chat_id"` } type JobPayloadVaultUserStatus struct { UserListID64 []int64 `json:"user_list"` ItemTypeListID64 []int64 `json:"item_type_list"` DepositChatID64 int64 `json:"deposit_chat_id"` UserID64 int64 `json:"user_id"` } type JobPayloadVaultItemStatus struct { ItemListID64 []int64 `json:"item_type_list"` DepositChatID64 int64 `json:"deposit_chat_id"` UserID64 int64 `json:"user_id"` } const ( userID64ChtWrsBot = 408101137 commandForwardMsg = 1 commandReplyMsg = 2 commandSendMsg = 3 commandDeleteMsg = 4 commandRefreshMsg = 5 commandSendDocument = 6 cmdParseModePlain = 1 cmdParseModeMarkDown = 2 cmdParseModeHTML = 3 objTypeUser = 1 objTypeGuild = 2 objTypeMessage = 3 objTypeWar = 4 objTypeWarReport = 5 objTypeJob = 6 objTypeItem = 7 objTypeCastle = 8 objTypeFair = 9 objTypeUnion = 10 objTypeTribute = 11 objTypeExperience = 12 objTypeQuest = 13 castleDeer = 1 castleDragon = 2 castleHighnest = 3 castleMoon = 4 castlePotato = 5 castleShark = 6 castleWolf = 7 objSubTypeUser = 101 objSubTypeGuild = 201 /* cacheObjSubType[`msg`] = 301 cacheObjSubType[`msg_war`] = 302 // from Chat Wars Reports (not done) objSubTypeMessageMiniWar = 303 // FIXME DELETE cacheObjSubType[`msg_guild_war`] = 304 // from Chat Wars Reports (not done) cacheObjSubType[`msg_report_req`] = 305 // /report (done) cacheObjSubType[`msg_report_ack`] = 306 // result from /report (done) cacheObjSubType[`msg_g_report_req`] = 307 // /g_report (done) cacheObjSubType[`msg_g_report_ack`] = 308 // result from /g_report (not done) cacheObjSubType[`msg_quest_res`] = 309 // result from going to quest (not done) cacheObjSubType[`msg_duel_fight`] = 310 // result from going to duel fight (done) cacheObjSubType[`msg_hero_req`] = 311 // /hero (done) cacheObjSubType[`msg_hero_ack`] = 312 // result from /hero (not done) cacheObjSubType[`msg_me_req`] = 313 // 🏅Me (done) cacheObjSubType[`msg_me_ack`] = 314 // result from 🏅Me (done) cacheObjSubType[`msg_inv_req`] = 315 // /inv (done) cacheObjSubType[`msg_inv_ack`] = 316 // result from /inv (not done) cacheObjSubType[`msg_pillage_inc`] = 317 // random incoming pillage (done) cacheObjSubType[`msg_pillage_go`] = 318 // ack from /go (done) cacheObjSubType[`msg_pillage_timeout`] = 319 // ack from lack of /go (done) cacheObjSubType[`msg_pillage_win`] = 320 // pillage successfully intercepted (done) cacheObjSubType[`msg_pillage_loss`] = 321 // pillage not intercepted (done) cacheObjSubType[`msg_tribute_inc`] = 322 // request for a /pledge (not done) cacheObjSubType[`msg_tribute_ack`] = 323 // pledge accepted (not done) cacheObjSubType[`msg_auction_announce`] = 324 // from Boris and Co, Ltd (done) cacheObjSubType[`msg_auction_upd_req`] = 325 // /l_123456 msg (not done) cacheObjSubType[`msg_auction_upd_ack`] = 326 // result from /l_123456 (not done) cacheObjSubType[`msg_time_ack`] = 327 // result from /time (done) cacheObjSubType[`msg_time_req`] = 328 // /time (done) cacheObjSubType[`msg_go`] = 329 // /go (done) cacheObjSubType[`msg_pledge`] = 330 // /pledge (done) cacheObjSubType[`msg_go_quest_req`] = 331 // 🌲Forest or 🍄Swamp or ⛰️Valley (not done) cacheObjSubType[`msg_fast_fight`] = 332 // ▶️Fast fight (not done) cacheObjSubType[`msg_go_arena`] = 333 // 📯Arena (not done) cacheObjSubType[`msg_top`] = 334 // any /topXX (not done) cacheObjSubType[`msg_menu`] = 335 // main menu (not done) cacheObjSubType[`msg_buy_req`] = 336 // /wtb_xx (done) cacheObjSubType[`msg_sell_req`] = 337 // /wts_xx (done) cacheObjSubType[`msg_orderbook_req`] = 338 // /t_xx (done) cacheObjSubType[`msg_orderbook_acl`] = 339 // orderbook summary (not done) cacheObjSubType[`msg_withdraw_req`] = 340 // /g_withdraw (done) cacheObjSubType[`msg_withdraw_code`] = 341 // code to receive (done) cacheObjSubType[`msg_withdraw_rcv`] = 342 // Withdraw "received" msg (done) cacheObjSubType[`msg_stock_req`] = 343 // /stock (done) cacheObjSubType[`msg_stock_ack`] = 344 // result from /stock (not done) cacheObjSubType[`msg_misc_req`] = 345 // /misc (done) cacheObjSubType[`msg_misc_ack`] = 346 // result from /misc (not done) cacheObjSubType[`msg_union_war`] = 347 // from Chat Wars Reports (not done) cacheObjSubType[`msg_tureport_req`] = 348 // /tu_report (not done) cacheObjSubType[`msg_tureport_ack`] = 349 // result from /tu_report (not done) cacheObjSubType[`msg_timeout`] = 350 // generic timeout for action cacheObjSubType[`msg_go_quest_ack`] = 351 // confirm quest destination/busyness (done) cacheObjSubType[`msg_groles_req`] = 352 // /g_roles (done) cacheObjSubType[`msg_groles_ack`] = 353 // result from /g_roles (done) cacheObjSubType[`msg_gstock_res_req`] = 354 // /g_stock_res (done) cacheObjSubType[`msg_gstock_alch_req`] = 355 // /g_stock_alch (done) cacheObjSubType[`msg_gstock_misc_req`] = 356 // /g_stock_misc (done) cacheObjSubType[`msg_gstock_rec_req`] = 357 // /g_stock_rec (done) cacheObjSubType[`msg_gstock_part_req`] = 358 // /g_stock_parts (done) cacheObjSubType[`msg_gstock_oth_req`] = 359 // /g_stock_other (done) cacheObjSubType[`msg_gstock_any_ack`] = 360 // result from any /g_stock_xxx (done) cacheObjSubType[`msg_gstock_req`] = 361 // /g_stock (done) cacheObjSubType[`msg_gstock_ack`] = 362 // result from /g_stock (done) cacheObjSubType[`msg_busy`] = 363 // too busy now cacheObjSubType[`msg_res_stock_req`] = 364 // 📦Resources or /stock (done) cacheObjSubType[`msg_alch_stock_req`] = 365 // ⚗️Alchemy (done) cacheObjSubType[`msg_misc_stock_req`] = 366 // 🗃Misc or /misc (done) cacheObjSubType[`msg_equip_stock_req`] = 367 // 🏷Equipment (done) cacheObjSubType[`msg_craft_stock_req`] = 368 // ⚒Crafting (done) cacheObjSubType[`msg_stock_empty`] = 369 // [empty] (done) cacheObjSubType[`msg_stock_any_ack`] = 370 // list of stock (done) cacheObjSubType[`msg_g_deposit_req`] = 371 // /g_deposit xx y (done) cacheObjSubType[`msg_g_deposit_ack`] = 372 // Deposited successfully: xx (y) (done) cacheObjSubType[`msg_castle_attack_req`] = 373 // Attack (done) cacheObjSubType[`msg_castle_attack_ack`] = 374 // Read for attack, select target (done) cacheObjSubType[`msg_castle_target_req`] = 375 // Castle selection (done) cacheObjSubType[`msg_castle_target_ack`] = 376 // Castle confirmation (done) cacheObjSubType[`msg_castle_def_req`] = 377 // Defend (done) cacheObjSubType[`msg_castle_def_ack`] = 378 // Defend confirmation (done) cacheObjSubType[`msg_back`] = 379 // Back (done) cacheObjSubType[`msg_castle_req`] = 380 // Castle (done) cacheObjSubType[`msg_castle_ack`] = 381 // Castle msg (not done) cacheObjSubType[`msg_exchange_req`] = 382 // ⚖Exchange (done) cacheObjSubType[`msg_exchange_ack`] = 383 // List of deals (done) cacheObjSubType[`msg_equip_req`] = 384 // /on_item (done) cacheObjSubType[`msg_equip_ack`] = 385 // Item equipped (done) cacheObjSubType[`msg_unequip_req`] = 386 // /off_item (done) cacheObjSubType[`msg_unequip_ack`] = 387 // Item unequipped (done) cacheObjSubType[`msg_no_stamina`] = 388 // Not enough stamina. (done) cacheObjSubType[`msg_orderbook_search`] = 389 // /t cacheObjSubType[`msg_quest_req`] = 390 // quest cacheObjSubType[`msg_quest_ack`] = 391 // forest/swamp/valley/arena cacheObjSubType[`msg_battle`] = 392 // wind is howling cacheObjSubType[`msg_order_cancel_req`] = 393 // /rm_xxx cacheObjSubType[`msg_order_cancel_ack`] = 394 // Cancelling order cacheObjSubType[`msg_stamina_restored`] = 395 // Stamina Restored cacheObjSubType[`msg_top`]Req = 396 cacheObjSubType[`msg_quest_res_ambush`] = 397 // result from going to quest with ambush (not done) cacheObjSubType[`msg_heal_up`] = 398 // you should heal up first (done) cacheObjSubType[`msg_arena_fight_ack`] = 399 // thirsty for blood ... (done) cacheObjSubType[`msg_go_arena`]Ack = 400 // Welcome to Arena! (done) cacheObjSubType[`msg_withdraw_nack`] = 401 // not enought items (done) cacheObjSubType[`msg_pillage_defeat`] = 402 // You tried stopping (done) cacheObjSubType[`msg_level_up_req`] = 403 // /level_up cacheObjSubType[`msg_level_up_ack`] = 404 */ objSubTypeJobPillage = 601 objSubTypeJobTribute = 602 objSubTypeJobStatus = 603 objSubTypeJobGWithdraw = 604 objSubTypeJobGStock = 605 objSubTypeJobRescanMsg = 606 objSubTypeJobSetJobDone = 607 objSubTypeJobMsgClient = 608 objSubTypeJobMsgRefresh = 609 objSubTypeJobBackupExport = 610 objSubTypeJobBackupImport = 611 objSubTypeJobGDeposit = 612 objSubTypeJobGDepositForward = 613 objSubTypeJobSaveRes = 614 objSubTypeJobVaultUserStatus = 615 objSubTypeJobVaultItemStatus = 616 objSubTypeJobFwdMsg = 617 // rename to objSubTypeJobMsgFwd objSubTypeJobSetDef = 618 objSubTypeJobMsgDel = 619 objSubTypeJobGetHammerTime = 620 objSubTypeItemResource = 701 objSubTypeItemAlch = 702 objSubTypeItemMisc = 703 objSubTypeItemRecipe = 704 objSubTypeItemPart = 705 objSubTypeItemOther = 706 objSubTypeItemUnique = 707 objSubTypeCastle = 801 objSubTypeFair = 901 objSubTypeUnion = 1001 objSubTypeTribute = 1101 objSubTypeExperience = 1201 objSubTypeQuestForest = 1301 objSubTypeQuestSwamp = 1302 objSubTypeQuestValley = 1303 objJobStatusCallBack = -1 objJobStatusNew = 0 objJobStatusPillageGo = 1 objJonStatusPending = 10 objJobStatusDone = 20 objJobPriority = 1 objJobPriorityRescanMsg = 2 objJobPriorityRescanAllMsg = 3 objJobPriorityBackup = 4 MQGetMsgWorkers = 12 MQCWMsgQueueSize = 100 SQLCWMsgWorkers = 6 SQLIdentifyMsgWorkers = 6 SQLMsgIdentifyQueueSize = 100 SQLMsgRescanJobSize = 25 JobWorkers = 12 JobQueueSize = 100 TGCmdWorkers = 3 TGCmdQueueSize = 100 MQTGCmdWorkers = 3 MQTGCmdQueueSize = 100 SQLJobSliceSize = 25 KeepAliveHeartBeatSeconds = 5 ) var ( chatWarsMonth = map[string]int{ "Wintar": 1, "Hornung": 2, "Lenzin": 3, "Ōstar": 4, "Winni": 5, "Brāh": 6, "Hewi": 7, "Aran": 8, "Witu": 9, "Wīndume": 10, "Herbist": 11, "Hailag": 12} chatWarsDaysSpecial = map[int]map[int]int{ 2: {1060: 29}, 4: {1060: 29}} chatWarsDays = map[int]int{ 1: 31, 2: 28, 3: 31, 4: 30, 5: 31, 6: 30, 7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31} )