8 Commits

3 changed files with 111 additions and 31 deletions

View File

@@ -1,4 +1,5 @@
require "http" require "http"
require "./utils.cr"
module Telepathy module Telepathy
class Bot class Bot
@@ -6,10 +7,6 @@ module Telepathy
Done Done
end end
enum ParseMode
Normal, Markdown, HTML
end
def initialize(@api_token : String) def initialize(@api_token : String)
@request_base = "https://api.telegram.org/bot#{@api_token}" @request_base = "https://api.telegram.org/bot#{@api_token}"
@this_user = uninitialized User? @this_user = uninitialized User?
@@ -18,44 +15,35 @@ module Telepathy
@last_update_id = nil @last_update_id = nil
@command_hooks = {} of String => Update, Array(String) -> Void @command_hooks = {} of String => Update, Array(String) -> Void
@message_hooks = [] of Update -> Void @message_hooks = [] of Update -> Void
@poll_start_hooks = [] of -> Void
@poll_end_hooks = [] of -> Void
@poll_channel = Channel(Int64?|Control).new @poll_channel = Channel(Int64?|Control).new
@update_channel = Channel(Array(Update)|Control).new @update_channel = Channel(Array(Update)|Control).new
@poll_running = false @poll_running = false
end end
def get_me def get_me
response = HTTP::Client.get(@request_base + "/getMe", Utils.get_me(@api_token)
headers: HTTP::Headers{"User-agent" => "Telepathy"})
return Response(User).from_json(response.body).result
end end
def get_updates(timeout = 0) def get_updates(timeout = 0)
update_data = {} of String => Int64 | Int32 | String Utils.get_updates(@api_token, timeout, @last_update_id)
update_data["timeout"] = timeout
@last_update_id.try { |id| update_data["offset"] = id }
response = HTTP::Client.get(@request_base + "/getUpdates",
headers: HTTP::Headers{"User-agent" => "Telepathy", "Content-type" => "application/json" },
body: update_data.to_json)
return Response(Array(Update)).from_json(response.body).result
end end
def send_message(chat_id : String | Int64, text : String, def send_message(chat_id : String | Int64, text : String,
parse_mode : ParseMode = ParseMode::Normal, parse_mode : Utils::ParseMode = Utils::ParseMode::Normal,
disable_web_preview : Bool = false, disable_web_preview : Bool = false,
disable_notification : Bool = false, disable_notification : Bool = false,
reply_to_message_id : Int64? = nil) reply_to_message_id : Int64? = nil)
message_data = { "chat_id" => chat_id, "text" => text } of String => Int64 | String | Bool Utils.send_message(@api_token, chat_id, text,
message_data["disable_web_preview"] = true if disable_web_preview parse_mode, disable_web_preview,
message_data["disable_notification"] = true if disable_notification disable_notification, reply_to_message_id)
if parse_mode == ParseMode::Markdown end
message_data["parse_mode"] = "Markdown"
elsif parse_mode == ParseMode::HTML def send_photo(chat_id : String | Int64, photo : String | File, caption : String? = nil,
message_data["parse_mode"] = "HTML" disable_notification : Bool = false,
end reply_to_message_id : Int64? = nil)
reply_to_message_id.try { |id| message_data["reply_to_message_id"] = id } Utils.send_photo(@api_token, chat_id, photo, caption, disable_notification, reply_to_message_id)
HTTP::Client.get(@request_base + "/sendMessage",
headers: HTTP::Headers{"User-agent" => "Telepathy", "Content-type" => "application/json" },
body: message_data.to_json)
end end
def command(command_name, &block: Update, Array(String) -> Void) def command(command_name, &block: Update, Array(String) -> Void)
@@ -66,6 +54,14 @@ module Telepathy
@message_hooks.push(block) @message_hooks.push(block)
end end
def poll_start(&block: -> Void)
@poll_start_hooks.push(block);
end
def poll_end(&block: -> Void)
@poll_end_hooks.push(block);
end
private def process_updates(updates) private def process_updates(updates)
updates.each do |update| updates.each do |update|
if message = update.message if message = update.message
@@ -74,8 +70,9 @@ module Telepathy
text = message.text.as String text = message.text.as String
if entity.offset == 0 && entity.type == "bot_command" if entity.offset == 0 && entity.type == "bot_command"
divider_index = (text.index /\s|@/) || text.size divider_index = (text.index /\s|@/) || text.size
first_space_index = (text.index /\s/) || text.size
command = text[1...divider_index] command = text[1...divider_index]
remaining = text[divider_index..text.size] remaining = text[first_space_index..text.size]
params = remaining.empty? ? ([] of String) : (remaining[1...remaining.size].split ' ') params = remaining.empty? ? ([] of String) : (remaining[1...remaining.size].split ' ')
@command_hooks[command]?.try { |command| command.call(update, params) } @command_hooks[command]?.try { |command| command.call(update, params) }
end end
@@ -93,7 +90,6 @@ module Telepathy
@last_update_id = action.nil? ? @last_update_id : action + 1 @last_update_id = action.nil? ? @last_update_id : action + 1
@update_channel.send get_updates 10 @update_channel.send get_updates 10
when Control when Control
@update_channel.send action
break break
end end
end end
@@ -106,6 +102,7 @@ module Telepathy
process_updates(item) process_updates(item)
@poll_channel.send item.last?.try &.update_id @poll_channel.send item.last?.try &.update_id
when Control when Control
@poll_channel.send item
break break
end end
end end
@@ -116,14 +113,16 @@ module Telepathy
if !@poll_running if !@poll_running
@poll_running = true @poll_running = true
spawn_workers spawn_workers
@poll_channel.send nil
@poll_start_hooks.each &.call
end end
@poll_channel.send nil
end end
def end_poll def end_poll
if @poll_running if @poll_running
@poll_running = false @poll_running = false
@poll_channel.send Control::Done @poll_channel.send Control::Done
@poll_end_hooks.each &.call
end end
end end
end end

View File

@@ -1,6 +1,6 @@
require "json" require "json"
require "./user.cr" require "./user.cr"
require "./char.cr" require "./chat.cr"
require "./message_entity.cr" require "./message_entity.cr"
require "./audio.cr" require "./audio.cr"
require "./document.cr" require "./document.cr"

81
src/telepathy/utils.cr Normal file
View File

@@ -0,0 +1,81 @@
module Telepathy::Utils
extend self
API_URL = "https://api.telegram.org/bot"
enum ParseMode
Normal, Markdown, HTML
end
def get_me(api_key : String)
response = HTTP::Client.get(API_URL + api_key + "/getMe",
headers: HTTP::Headers{"User-agent" => "Telepathy"})
return Response(User).from_json(response.body).result
end
def get_updates(api_key : String, timeout = 0, last_update_id : Int? = nil)
update_data = {} of String => Int64 | Int32 | String
update_data["timeout"] = timeout
last_update_id.try { |id| update_data["offset"] = id }
response = HTTP::Client.get(API_URL + api_key + "/getUpdates",
headers: HTTP::Headers{"User-agent" => "Telepathy",
"Content-type" => "application/json" },
body: update_data.to_json)
return Response(Array(Update)).from_json(response.body).result
end
def send_message(api_key : String, chat_id : String | Int64, text : String,
parse_mode : ParseMode = ParseMode::Normal,
disable_web_preview : Bool = false,
disable_notification : Bool = false,
reply_to_message_id : Int64? = nil)
message_data = { "chat_id" => chat_id, "text" => text } of String => Int64 | String | Bool
message_data["disable_web_preview"] = true if disable_web_preview
message_data["disable_notification"] = true if disable_notification
if parse_mode == ParseMode::Markdown
message_data["parse_mode"] = "Markdown"
elsif parse_mode == ParseMode::HTML
message_data["parse_mode"] = "HTML"
end
reply_to_message_id.try { |id| message_data["reply_to_message_id"] = id }
HTTP::Client.get(API_URL + api_key + "/sendMessage",
headers: HTTP::Headers{"User-agent" => "Telepathy", "Content-type" => "application/json" },
body: message_data.to_json)
end
def send_photo(api_key : String, chat_id : String | Int64, photo : String | File, caption : String? = nil,
disable_notification : Bool = false,
reply_to_message_id : Int64? = nil)
IO.pipe do |reader, writer|
channel = Channel(String).new(1)
spawn do
HTTP::FormData.build(writer) do |formdata|
channel.send(formdata.content_type)
case chat_id
when String
formdata.field("chat_id", chat_id.as(String))
when Int64
formdata.field("chat_id", chat_id.as(Int64).to_s)
end
formdata.field("disable_notification", disable_notification.to_s)
caption.try { |caption| formdata.field("caption", caption) }
reply_to_message_id.try { |id| formdata.field("reply_to_message_id", id) }
case photo
when String
formdata.field("photo", photo.as(String))
when File
photo_file = photo.as(File)
formdata.file("photo", photo_file,
HTTP::FormData::FileMetadata.new(filename: File.basename(photo_file.path)))
end
end
writer.close
end
response = HTTP::Client.get(API_URL + api_key + "/sendPhoto",
headers: HTTP::Headers{"User-agent" => "Telepathy",
"Content-type" => channel.receive },
body: reader)
end
end
end