addServer(MEMCACHED_HOST, MEMCACHED_PORT)) { error("something is wrong connecting to memcached daemon"); } // Structure of managed telegram messages /* * **Generic message** * $row * |_ message § Message * |_ message_id § integer * |_ chat § chat * |_ id (chatID) § integer * |_ username (if set - user name for private chat, else channel or group name) § string * |_ ...not relevant things... * |_ from § User * |_ id (userID) § integer * |_ username (if set - user name) § string * |_ .... * |_ forward_from § User * |_ forward_from_chat § Chat * |_ text § string (message_text) * |_ entities: [{"offset":...,"length":...,"type":"italic"}] * |_ photo, location, video * * **Callback Query** * $row * |_ callback_query * |_ id § integer * |_ from § User * |_ message § Message * |_ inline_message_id § integer (inlineID) * |_ ... */ // HANDLE A CALLBACK if (isset ($row->callback_query)) { DEBUG and botlog('handle callback query'); $chatID = $row->callback_query->message->chat->id; // Check if it was thrown by admitted users in_array($row->callback_query->message->chat->username, BOT_ALLOWED_USERS) or not_authorized($chatID); botlog('Ok authorized user pass!'); // Answer to the callback query (remove the loading sign) info("Callback Query, answering"); $url = API_URL.API_TOKEN."/answerCallbackQuery?callback_query_id=".$row->callback_query->id; file_get_contents($url); // Do something - insert here useful variables $callback_data = $row->callback_query->data; $inlineID = $row->callback_query->message->message_id; $inlineID_old = $mc->get($chatID.MC_INLINE_ID); // check if request comes from active inline item if ($inlineID != $inlineID_old) { // expired session // If called callback comes from an old inline item do not answer if (DEBUG) warning("That's an old callback!"); die(); } // Finally, check actual state and do things $status = $mc->get($chatID.MC_STATUS); if (!$status) { $mc->set($chatID.MC_STATUS, STATE_IDLE) or die(); // set status or force it to STATE_IDLE $status = STATE_IDLE; } if (DEBUG) info("Starting from status $status"); // Callback state machine switch ($status) { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * entering STATE_IDLE - callback * * you can: list and delete messages given by /list * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ case STATE_IDLE: switch ($callback_data){ case MSG_DELETE: // remove current message $sql = new Sqlite3(DBFILE); $IDdelete = $mc->get($chatID.MC_DELETE_SCHEDULED_ID) or error("No ID to delete"); $sql->query("DELETE FROM telegram_post WHERE ID=$IDdelete") or error("Can't make the query, SQL error ".$sql->lastErrorMsg()); $sql->close(); $offset = $mc->get($chatID.MC_LIST_NUMBER); if ($offset > 0) { $offset--; } break; case '>': $offset = $mc->get($chatID.MC_LIST_NUMBER); $offset++; // nobody cares if goes over # of programmed messages break; case '<': $offset = $mc->get($chatID.MC_LIST_NUMBER); if ($offset > 0) $offset--; break; default: if (DEBUG) warning("Callback request is not accepted in status $status"); die(); } $archivedMsg = navigateMsgArchive($offset, $mc, $chatID); $answer = editMessageText($chatID, $inlineID, $archivedMsg['text'], "HTML", false, $archivedMsg['kbd'] ); $answer = json_decode($answer); $mc->set($chatID.MC_LIST_NUMBER, $offset) or $mc->replace($chatID.MC_LIST_NUMBER, $offset); die(); break; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * END STATE_IDLE * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * entering STATE_MSG_ANSWER - callback * you can: (after you sent a message) choose if forward to * channel or schedule it * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ case STATE_MSG_ANSWER: // only accessible by callback /////// spostare di là if (!(isset($callback_data))) { //$mc->replace($chatID.MC_STATUS, STATE_IDLE); wrong_action($chatID); die(); } switch ($callback_data) { case MSG_YES: // forward previously sent message // set status? $mc->replace($chatID.MC_STATUS, STATE_IDLE) or error("Something is wrong with memcached"); $msg = $mc->get($chatID.MC_FORWARD_MSG) or error("Can't forward message\n"); forwardMessage(API_CHANNEL_ID, $msg->message->chat->id, $msg->message->message_id, FORWARD_SILENT); // Remove kbd $text = "Invio effettuato correttamente"; editMessageText($chatID, $inlineID, $text); //~ $url = API_URL.API_TOKEN."/editMessageText?chat_id=".($chatID). //~ "&message_id=".$inlineID . //~ "&text=".urlencode($text); //~ file_get_contents($url); break; case MSG_NO: $status = $mc->replace($chatID.MC_STATUS, STATE_IDLE) or error("Something is wrong with memcached"); // destroy saved message? $text = "Invio annullato correttamente"; editMessageText($chatID, $inlineID, $text); break; case MSG_SCHEDULE: // == from here can be put in a function == info('status MSG_SCHEDULE'); $status = $mc->replace($chatID.MC_STATUS, STATE_WAIT_DATE) or error(__FILE__ . __LINE__); $currentTab = array( 'month' => date('n'), 'year' => date('Y'), 'day' => null ); $reply = getCalendarTab($currentTab['month'], $currentTab['year']); info('calendar tab: ' . $reply); $mc->set($chatID.MC_DATE_MSG, $currentTab) or $mc->replace($chatID.MC_DATE_MSG, $currentTab) or error(__FILE__ . __LINE__); $text="Quando vuoi inviare il messaggio?"; editMessageText($chatID, $inlineID, $text, "HTML", false, $reply); $mc->set($chatID.MC_INLINE_ID, $inlineID) or $mc->replace($chatID.MC_INLINE_ID, $inlineID) or error("Something is wrong with memcached"); // == end == break; } break; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * END STATE_MSG_ANSWER * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * entering STATE_WAIT_DATE - callback * you can: choose the date of scheduled message(s) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ case STATE_WAIT_DATE: $currentTab = $mc->get($chatID.MC_DATE_MSG) or error("Something is wrong with memcached"); // two cases: change page or confirm date if ($callback_data == '>') { // add 1 to month if ($currentTab['month'] < 12) $currentTab['month'] +=1; else { $currentTab['month'] = 1; $currentTab['year'] +=1; } $mc->set($chatID.MC_DATE_MSG, $currentTab) or $mc->replace($chatID.MC_DATE_MSG, $currentTab) or error("Something is wrong with memcached"); $text = "Il calendario del ".$currentTab['month']."-".$currentTab['year']; // get new inline container } elseif ($callback_data == '<') { if ($currentTab['month'] > 1) $currentTab['month'] -=1; else { $currentTab['month'] = 12; $currentTab['year'] -=1; } $text = "Il calendario del ".$currentTab['month']."-".$currentTab['year']; $mc->set($chatID.MC_DATE_MSG, $currentTab) or $mc->replace($chatID.MC_DATE_MSG, $currentTab) or error("Something is wrong with memcached"); } elseif ($callback_data == 'null') { // not a valid button (like week days buttons) die(); } elseif ($callback_data == MSG_ABORT) { $text = "Invio annullato correttamente"; editMessageText($chatID, $inlineID, $text); $mc->replace($chatID.MC_STATUS, STATE_IDLE) or die(); die(); } else { // read current day, verify it and then change state if (!$callback_data = intval($callback_data)) error("Not a numerical day, how you did it?!?"); $currentTab['day'] = $callback_data; $mc->set($chatID.MC_DATE_MSG, $currentTab) or $mc->replace($chatID.MC_DATE_MSG, $currentTab) or die ("Failed to save data at Memcached server"); // Display time inline keyboard $kbd = new InlineKbd; $firstHour = date('Ynj') == $currentTab['year'].$currentTab['month'].$currentTab['day']? date('G')+1 : 8; for ($i = 8; $i <= 21; $i++) { if ($i < $firstHour) $kbd->insertItem(" ", "null"); else $kbd->insertItem("$i".":00", $i); if ($i == 14) $kbd->pushLine(); } $kbd->pushLine(); $kbd->insertItem("$EMOJI_X Annulla", MSG_ABORT); $kbd->pushLine(); $text="A che ora vuoi inviare il messaggio?"; //~ $url = API_URL.API_TOKEN."/editMessageText?chat_id=".($chatID). //~ "&message_id=".$inlineID . //~ "&text=".urlencode($text)."&reply_markup=".$reply; //~ file_get_contents($url); editMessageText($chatID, $inlineID, $text, "HTML", false, $kbd->getKeyboard()); $mc->replace($chatID.MC_STATUS, STATE_WAIT_TIME) or die(); break; } // If answer is < or > remain in current state and update calendar $reply = getCalendarTab($currentTab['month'], $currentTab['year']); //~ $url = API_URL.API_TOKEN."/editMessageText?chat_id=".($chatID). //~ "&message_id=".$inlineID . //~ "&text=".urlencode($text)."&reply_markup=".$reply; //~ file_get_contents($url); editMessageText($chatID, $inlineID, $text, "HTML", false, $reply); $mc->set($chatID.MC_INLINE_ID, $inlineID) or $mc->replace($chatID.MC_INLINE_ID, $inlineID); break; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * END STATE_WAIT_DATE * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * entering STATE_WAIT_TIME - callback * you can: choose the time of scheduled message(s) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ case STATE_WAIT_TIME: $mc->replace($chatID.MC_STATUS, STATE_IDLE); if ($callback_data == MSG_ABORT) { $text = "Invio annullato correttamente"; editMessageText($chatID, $inlineID, $text); $mc->replace($chatID.MC_STATUS, STATE_IDLE) or die(); die(); } elseif (!$time = intval($callback_data)) { if (DEBUG) botlog("[EE] Not a numeric time\n"); die(); } $currentTab = $mc->get($chatID.MC_DATE_MSG); $msg = $mc->get($chatID.MC_FORWARD_MSG); // Message preview in scheduling list is not formatted! $text = htmlspecialchars($msg->message->text); $dateObj = DateTime::createFromFormat('Y-n-j-G-e', $currentTab['year'] . '-' . $currentTab['month'] . '-' . $currentTab['day'] . '-' . $callback_data . '-Europe/Rome'); $dateObj->setTimezone(new DateTimeZone('UTC')); $formattedDate = $dateObj->format('Y-m-d H:i:s'); // add to database (mysql) $sql = new SQLite3(DBFILE); // Build up mysql query $value = "("; $value .= $msg->message->message_id; $value .= ","; $value .= $msg->message->chat->id; $value .= ","; $value .= "'$formattedDate'"; $value .= ","; if (isset($text)) $value .= "'".$sql->escapeString($text)."'"; else $value .= "''"; $value .= ","; if (isset($msg->message->forward_from->username)) $value .= "'".$sql->escapeString($msg->message->forward_from->username)."'"; elseif (isset($msg->message->from->username)) $value .= "'".$sql->escapeString($msg->message->from->username)."'"; else $value .= "''"; $value .= ");"; $query = "INSERT INTO telegram_post (MessageID,ChatID,DateTime,Text,Author) VALUES ".$value; info('saving post in database, query: ' . $query); $sql->query($query) or error("Can't make the query, SQL error ".$sql->lastErrorMsg()); $sql->close(); $text = "Programmazione avvenuta con successo"; editMessageText($chatID, $inlineID, $text); break; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * END STATE_WAIT_TIME * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ } // HANDLE A COMMON MESSAGE (text/commands/photos...) elseif (isset($row->message)) { info('handling common message'); // Load variables $chatID = $row->message->chat->id; // Check if it was thrown by admitted users if (in_array($row->message->from->username, BOT_ALLOWED_USERS)) { info('authorized user ok'); // Finally, check actual state and do things $status = $mc->get($chatID.MC_STATUS); if (!$status) { $mc->set($chatID.MC_STATUS, STATE_IDLE) or die(); // set status or force it to STATE_IDLE $status = STATE_IDLE; } info("Starting from status $status"); // Reset CMD if (($command = getCommand($row->message))['command'] == '/reset') { warning("Reset command received"); $all_keys = $mc->getAllKeys(); foreach ($all_keys as $index => $key) { if (strpos($key,$chatID) !== 0) { $mc->delete($key); info("Deleting $key"); } else { unset($all_keys[$index]); } } sendMessage($chatID, "Reset completato"); } else { switch($status) { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * entering STATE_IDLE - message * * you can: give a command, send messages to forward * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ case STATE_IDLE: if (($command = getCommand($row->message)) != null) { info("Command received: $command[command] with options $command[options]"); switch ($command['command']) { case '/help': case '/start': // Send some explainations about this bot sendMessage($chatID,$WELCOME_MESSAGE, "HTML"); break; case '/edit': // not yet implemented sendMessage($chatID, "Bravo! Hai trovato un comando non ancora implementato. Apri il codice su GitHub e lavoraci su"); break; case '/list': $archivedMsg = navigateMsgArchive(0, $mc, $chatID); $answer = sendMessage($chatID, $archivedMsg['text'], "HTML", false, false, null, $archivedMsg['kbd']); $answer = json_decode($answer); // Should forward the message if is not pure text //forwardMessage($chatID, $row['ChatID'], $row['MessageID']); $mc->set($chatID.MC_INLINE_ID, $answer->result->message_id) or $mc->replace($chatID.MC_INLINE_ID, $answer->result->message_id); $mc->set($chatID.MC_LIST_NUMBER, 0) or $mc->replace($chatID.MC_LIST_NUMBER, 0); break; default: sendMessage($chatID,"Non è un comando valido, leggi i comandi digitando /help"); warning("$command[command] is not a valid command"); } } else { // not a command (text?) // you probably want to forward a message? // Check what kind of message is this $kbd = new InlineKbd; $kbd->insertItem("SI ".$EMOJI_THUMBSUP,MSG_YES); $kbd->insertItem("NO ".$EMOJI_THUMBSDOWN,MSG_NO); $kbd->insertItem("Programma ".$EMOJI_CLOCK,MSG_SCHEDULE); $kbd->pushLine(); // This bot accepts only text, images, videos and locations if (!(isset($row->message->photo) or isset($row->message->location) or isset($row->message->video) or isset($row->message->text) )) { sendMessage($chatID, "Questa roba non va bene"); error("Not supported content"); // Program stops here } $mc->set($chatID.MC_FORWARD_MSG, $row) or $mc->replace($chatID.MC_FORWARD_MSG, $row) or error("Something is wrong with memcached"); // SetUp inline messages (OK, NO, SCHEDULE) $text="Vuoi condividere sul canale?"; // NOME CANALE ".API_CHANNEL_ID."?"; $query = sendMessage($row->message->chat->id, $text, null, false, false, null, $kbd->getKeyboard()); //~ $answer = file_get_contents($query); $answer = json_decode($query); $inlineID = $answer->result->message_id; if (DEBUG) info("Callback message id: $inlineID"); $mc->replace($chatID.MC_STATUS, STATE_MSG_ANSWER); $mc->set($chatID.MC_INLINE_ID, $inlineID) or $mc->replace($chatID.MC_INLINE_ID, $inlineID); } break; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * END STATE_IDLE * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ default: wrong_action($chatID); } } } else not_authorized($chatID); } // a not supported message (i.e. from a channel) else { error("This is not a supported request. Please check JSON dump"); } ?>