diff options
| author | 2025-07-22 08:23:22 +0300 | |
|---|---|---|
| committer | 2025-07-22 08:23:22 +0300 | |
| commit | 1d1df7b540721d58db174764a0f720e3c638a67b (patch) | |
| tree | 44bbfc972dac04e32729a3065c90dea0334149d8 /src | |
| parent | replace trimming with just checking if the string is whitespace (diff) | |
| download | ukkobot-1d1df7b540721d58db174764a0f720e3c638a67b.tar.gz ukkobot-1d1df7b540721d58db174764a0f720e3c638a67b.tar.xz ukkobot-1d1df7b540721d58db174764a0f720e3c638a67b.zip | |
Whitelist allowed inline bots
Also update to Zig 0.14.1
Also don't send the animations rn
Diffstat (limited to 'src')
| -rw-r--r-- | src/Bot.zig | 12 | ||||
| -rw-r--r-- | src/Config.zig | 8 | ||||
| -rw-r--r-- | src/json.zig | 6 | ||||
| -rw-r--r-- | src/main.zig | 120 | ||||
| -rw-r--r-- | src/types.zig | 1 | ||||
| -rw-r--r-- | src/types/DeleteMessageParams.zig | 3 | ||||
| -rw-r--r-- | src/utils.zig | 40 |
7 files changed, 145 insertions, 45 deletions
diff --git a/src/Bot.zig b/src/Bot.zig index 8e69579..b0eb972 100644 --- a/src/Bot.zig +++ b/src/Bot.zig | |||
| @@ -47,6 +47,10 @@ pub fn deinit(self: *Bot) void { | |||
| 47 | self.* = undefined; | 47 | self.* = undefined; |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | pub inline fn deleteMessage(self: *Bot, args: types.DeleteMessageParams) !void { | ||
| 51 | (try self.post(bool, "deleteMessage", args)).deinit(); | ||
| 52 | } | ||
| 53 | |||
| 50 | pub inline fn editMessageText(self: *Bot, args: types.EditMessageTextParams) !Parsed(types.Message) { | 54 | pub inline fn editMessageText(self: *Bot, args: types.EditMessageTextParams) !Parsed(types.Message) { |
| 51 | return self.post(types.Message, "editMessageText", args); | 55 | return self.post(types.Message, "editMessageText", args); |
| 52 | } | 56 | } |
| @@ -142,7 +146,7 @@ fn call( | |||
| 142 | return res; | 146 | return res; |
| 143 | } else |err| { | 147 | } else |err| { |
| 144 | std.log.warn("error when performing call: {}", .{err}); | 148 | std.log.warn("error when performing call: {}", .{err}); |
| 145 | if (tries == 20) { | 149 | if (tries == 4) { |
| 146 | return err; | 150 | return err; |
| 147 | } | 151 | } |
| 148 | } | 152 | } |
| @@ -218,9 +222,9 @@ fn wrappedCall( | |||
| 218 | 222 | ||
| 219 | fn intoQueryString(allocator: Allocator, data: anytype) !?[]u8 { | 223 | fn intoQueryString(allocator: Allocator, data: anytype) !?[]u8 { |
| 220 | return switch (@typeInfo(@TypeOf(data))) { | 224 | return switch (@typeInfo(@TypeOf(data))) { |
| 221 | .Null => null, | 225 | .null => null, |
| 222 | .Optional => if (data) |d| intoQueryString(allocator, d) else null, | 226 | .optional => if (data) |d| intoQueryString(allocator, d) else null, |
| 223 | .Struct => |s| { | 227 | .@"struct" => |s| { |
| 224 | var sb = ArrayList(u8).init(allocator); | 228 | var sb = ArrayList(u8).init(allocator); |
| 225 | defer sb.deinit(); | 229 | defer sb.deinit(); |
| 226 | 230 | ||
diff --git a/src/Config.zig b/src/Config.zig index 4deebbc..f9d6dab 100644 --- a/src/Config.zig +++ b/src/Config.zig | |||
| @@ -71,13 +71,13 @@ pub fn load(parent_allocator: Allocator, filename: []const u8) !Wrapper { | |||
| 71 | 71 | ||
| 72 | const Nullable = blk: { | 72 | const Nullable = blk: { |
| 73 | const template = @typeInfo(Config); | 73 | const template = @typeInfo(Config); |
| 74 | var fields: [template.Struct.fields.len]std.builtin.Type.StructField = undefined; | 74 | var fields: [template.@"struct".fields.len]std.builtin.Type.StructField = undefined; |
| 75 | for (template.Struct.fields, 0..) |template_field, field_idx| { | 75 | for (template.@"struct".fields, 0..) |template_field, field_idx| { |
| 76 | fields[field_idx] = template_field; | 76 | fields[field_idx] = template_field; |
| 77 | fields[field_idx].type = ?template_field.type; | 77 | fields[field_idx].type = ?template_field.type; |
| 78 | } | 78 | } |
| 79 | var new = template; | 79 | var new = template; |
| 80 | new.Struct.fields = &fields; | 80 | new.@"struct".fields = &fields; |
| 81 | new.Struct.decls = &.{}; | 81 | new.@"struct".decls = &.{}; |
| 82 | break :blk @Type(new); | 82 | break :blk @Type(new); |
| 83 | }; | 83 | }; |
diff --git a/src/json.zig b/src/json.zig index 9252344..ff07531 100644 --- a/src/json.zig +++ b/src/json.zig | |||
| @@ -50,7 +50,7 @@ pub fn makeJsonParseFromValue(T: type) JsonParseFromValue(T) { | |||
| 50 | 50 | ||
| 51 | pub fn makeJsonParseFromValueWithTag(T: type, comptime tag: [:0]const u8) JsonParseFromValue(T) { | 51 | pub fn makeJsonParseFromValueWithTag(T: type, comptime tag: [:0]const u8) JsonParseFromValue(T) { |
| 52 | const union_info = switch (@typeInfo(T)) { | 52 | const union_info = switch (@typeInfo(T)) { |
| 53 | .Union => |info| blk: { | 53 | .@"union" => |info| blk: { |
| 54 | if (info.tag_type == null) { | 54 | if (info.tag_type == null) { |
| 55 | @compileError("Only tagged unions supported, got '" ++ @typeName(T) ++ "'"); | 55 | @compileError("Only tagged unions supported, got '" ++ @typeName(T) ++ "'"); |
| 56 | } | 56 | } |
| @@ -62,12 +62,12 @@ pub fn makeJsonParseFromValueWithTag(T: type, comptime tag: [:0]const u8) JsonPa | |||
| 62 | const TagType = union_info.tag_type.?; | 62 | const TagType = union_info.tag_type.?; |
| 63 | 63 | ||
| 64 | const Base = blk: { | 64 | const Base = blk: { |
| 65 | const info = std.builtin.Type{ .Struct = .{ | 65 | const info = std.builtin.Type{ .@"struct" = .{ |
| 66 | .layout = .auto, | 66 | .layout = .auto, |
| 67 | .fields = &.{.{ | 67 | .fields = &.{.{ |
| 68 | .name = tag, | 68 | .name = tag, |
| 69 | .type = TagType, | 69 | .type = TagType, |
| 70 | .default_value = null, | 70 | .default_value_ptr = null, |
| 71 | .is_comptime = false, | 71 | .is_comptime = false, |
| 72 | .alignment = 0, | 72 | .alignment = 0, |
| 73 | }}, | 73 | }}, |
diff --git a/src/main.zig b/src/main.zig index b164284..9e1de47 100644 --- a/src/main.zig +++ b/src/main.zig | |||
| @@ -46,6 +46,27 @@ fn loadConfig(allocator: Allocator, filename: []const u8) !std.json.Parsed(Confi | |||
| 46 | ); | 46 | ); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | fn reportError(bot: *Bot, msg: types.Message, err: anyerror) !void { | ||
| 50 | std.log.err("While handling {}: {}", .{ msg, err }); | ||
| 51 | const msgStr = try std.json.stringifyAlloc(bot.allocator, msg, .{ | ||
| 52 | .whitespace = .indent_2, | ||
| 53 | .emit_null_optional_fields = false, | ||
| 54 | }); | ||
| 55 | defer bot.allocator.free(msgStr); | ||
| 56 | |||
| 57 | const devMsg = try std.fmt.allocPrint(bot.allocator, "<code>{}</code> while handling\n<pre>{s}</pre>", .{ err, msgStr }); | ||
| 58 | defer bot.allocator.free(devMsg); | ||
| 59 | |||
| 60 | bot.sendMessage_(.{ | ||
| 61 | .chat_id = bot.config.dev_group, | ||
| 62 | .text = devMsg, | ||
| 63 | .parse_mode = .html, | ||
| 64 | }) catch |err2| { | ||
| 65 | std.log.err("While trying to report the error: {}", .{err2}); | ||
| 66 | return err2; | ||
| 67 | }; | ||
| 68 | } | ||
| 69 | |||
| 49 | fn wrappedMain(bot: *Bot) !void { | 70 | fn wrappedMain(bot: *Bot) !void { |
| 50 | try bot.sendMessage_(.{ | 71 | try bot.sendMessage_(.{ |
| 51 | .chat_id = bot.config.dev_group, | 72 | .chat_id = bot.config.dev_group, |
| @@ -63,8 +84,9 @@ fn wrappedMain(bot: *Bot) !void { | |||
| 63 | defer gup.offset = update.update_id + 1; | 84 | defer gup.offset = update.update_id + 1; |
| 64 | 85 | ||
| 65 | if (update.message) |message| { | 86 | if (update.message) |message| { |
| 66 | // TODO: Catch minor errors, report them | 87 | onMessage(bot, message) catch |err| { |
| 67 | try onMessage(bot, message); | 88 | try reportError(bot, message, err); |
| 89 | }; | ||
| 68 | } | 90 | } |
| 69 | } | 91 | } |
| 70 | } | 92 | } |
| @@ -80,7 +102,63 @@ fn wrappedMain(bot: *Bot) !void { | |||
| 80 | }); | 102 | }); |
| 81 | } | 103 | } |
| 82 | 104 | ||
| 105 | const allowed_inline_bots = [_]i64{ | ||
| 106 | 90832338, // @vid | ||
| 107 | 109158646, // @bing | ||
| 108 | 114528005, // @pic | ||
| 109 | 140267078, // @gif | ||
| 110 | 154595593, // @wiki | ||
| 111 | 184730458, // @UnitConversionBot | ||
| 112 | 296635833, // @lastfmrobot | ||
| 113 | 595898211, // @DeezerMusicBot | ||
| 114 | 870410041, // @HowGayBot | ||
| 115 | 7904498194, // @tanstiktokbot | ||
| 116 | }; | ||
| 117 | |||
| 118 | comptime { | ||
| 119 | std.testing.expect(std.sort.isSorted( | ||
| 120 | i64, | ||
| 121 | &allowed_inline_bots, | ||
| 122 | {}, | ||
| 123 | std.sort.asc(i64), | ||
| 124 | )) catch unreachable; | ||
| 125 | } | ||
| 126 | |||
| 127 | fn orderI64(a: i64, b: i64) std.math.Order { | ||
| 128 | return std.math.order(a, b); | ||
| 129 | } | ||
| 130 | |||
| 131 | fn isAllowedInlineBot(id: i64) bool { | ||
| 132 | return std.sort.binarySearch( | ||
| 133 | i64, | ||
| 134 | &allowed_inline_bots, | ||
| 135 | id, | ||
| 136 | orderI64, | ||
| 137 | ) != null; | ||
| 138 | } | ||
| 139 | |||
| 83 | fn onMessage(bot: *Bot, msg: types.Message) !void { | 140 | fn onMessage(bot: *Bot, msg: types.Message) !void { |
| 141 | if (msg.via_bot) |via| { | ||
| 142 | if (!isAllowedInlineBot(via.id)) { | ||
| 143 | std.log.info("Deleting an unallowed inline bot message from {?s} {}", .{ via.username, via.id }); | ||
| 144 | try bot.deleteMessage(.{ | ||
| 145 | .chat_id = msg.chat.id, | ||
| 146 | .message_id = msg.message_id, | ||
| 147 | }); | ||
| 148 | |||
| 149 | const text = try std.fmt.allocPrint(bot.allocator, "Deleted a message sent by inline bot @{?s} <code>{}</code>", .{ via.username, via.id }); | ||
| 150 | defer bot.allocator.free(text); | ||
| 151 | |||
| 152 | try bot.sendMessage_(.{ | ||
| 153 | .chat_id = bot.config.dev_group, | ||
| 154 | .text = text, | ||
| 155 | .parse_mode = .html, | ||
| 156 | }); | ||
| 157 | |||
| 158 | return; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 84 | if (msg.text) |text| { | 162 | if (msg.text) |text| { |
| 85 | try onTextMessage(bot, msg, text); | 163 | try onTextMessage(bot, msg, text); |
| 86 | } | 164 | } |
| @@ -94,12 +172,14 @@ fn onMessage(bot: *Bot, msg: types.Message) !void { | |||
| 94 | 172 | ||
| 95 | fn onNewMember(bot: *Bot, msg: types.Message, new_member: types.User) !void { | 173 | fn onNewMember(bot: *Bot, msg: types.Message, new_member: types.User) !void { |
| 96 | if (new_member.id == try bot.getId()) { | 174 | if (new_member.id == try bot.getId()) { |
| 175 | return; | ||
| 176 | // TODO: | ||
| 97 | // Bot is added to a new group | 177 | // Bot is added to a new group |
| 98 | return bot.sendAnimation_(.{ | 178 | // return bot.sendAnimation_(.{ |
| 99 | .chat_id = msg.chat.id, | 179 | // .chat_id = msg.chat.id, |
| 100 | // TODO: lol | 180 | // // TODO: lol |
| 101 | .animation = "CgACAgQAAx0CVcPEEgACDC5mo7YHMgOE2n3qo3e9UOyd4N-uxQACNAMAAlbuDFMRWj9LxNLBkDUE", | 181 | // .animation = "CgACAgQAAx0CVcPEEgACDC5mo7YHMgOE2n3qo3e9UOyd4N-uxQACNAMAAlbuDFMRWj9LxNLBkDUE", |
| 102 | }); | 182 | // }); |
| 103 | } | 183 | } |
| 104 | 184 | ||
| 105 | var sb = ArrayList(u8).init(bot.allocator); | 185 | var sb = ArrayList(u8).init(bot.allocator); |
| @@ -111,13 +191,25 @@ fn onNewMember(bot: *Bot, msg: types.Message, new_member: types.User) !void { | |||
| 111 | try new_member.writeFormattedName(w); | 191 | try new_member.writeFormattedName(w); |
| 112 | try w.writeAll("! Be on your bestest behaviour now!!"); | 192 | try w.writeAll("! Be on your bestest behaviour now!!"); |
| 113 | 193 | ||
| 114 | try bot.sendAnimation_(.{ | 194 | // TODO: |
| 195 | // try bot.sendAnimation_(.{ | ||
| 196 | // .chat_id = msg.chat.id, | ||
| 197 | // // TODO: lol | ||
| 198 | // .animation = "CgACAgQAAx0CVcPEEgACC9Vmo6_zCxMp3ZNXSMM1nI6nMkIhgwACNwMAAtDmDFMop6BHmV7lUTUE", | ||
| 199 | // .caption = sb.items, | ||
| 200 | // .parse_mode = .html, | ||
| 201 | // .show_caption_above_media = true, | ||
| 202 | // .reply_parameters = .{ | ||
| 203 | // .allow_sending_without_reply = true, | ||
| 204 | // .message_id = msg.message_id, | ||
| 205 | // .chat_id = msg.chat.id, | ||
| 206 | // }, | ||
| 207 | // }); | ||
| 208 | |||
| 209 | try bot.sendMessage_(.{ | ||
| 115 | .chat_id = msg.chat.id, | 210 | .chat_id = msg.chat.id, |
| 116 | // TODO: lol | 211 | .text = sb.items, |
| 117 | .animation = "CgACAgQAAx0CVcPEEgACC9Vmo6_zCxMp3ZNXSMM1nI6nMkIhgwACNwMAAtDmDFMop6BHmV7lUTUE", | ||
| 118 | .caption = sb.items, | ||
| 119 | .parse_mode = .html, | 212 | .parse_mode = .html, |
| 120 | .show_caption_above_media = true, | ||
| 121 | .reply_parameters = .{ | 213 | .reply_parameters = .{ |
| 122 | .allow_sending_without_reply = true, | 214 | .allow_sending_without_reply = true, |
| 123 | .message_id = msg.message_id, | 215 | .message_id = msg.message_id, |
| @@ -201,8 +293,8 @@ fn onTextMessage(bot: *Bot, msg: types.Message, text: []const u8) !void { | |||
| 201 | } else if (std.ascii.startsWithIgnoreCase(text, "big ")) { | 293 | } else if (std.ascii.startsWithIgnoreCase(text, "big ")) { |
| 202 | const the_text = text[4..]; | 294 | const the_text = text[4..]; |
| 203 | if (!try utils.isTgWhitespaceStr(the_text)) { | 295 | if (!try utils.isTgWhitespaceStr(the_text)) { |
| 204 | const cd = try utils.getCD(); | 296 | const lc = try utils.getLetterCasing(); |
| 205 | const uppercased = try cd.toUpperStr(bot.allocator, the_text); | 297 | const uppercased = try lc.toUpperStr(bot.allocator, the_text); |
| 206 | defer bot.allocator.free(uppercased); | 298 | defer bot.allocator.free(uppercased); |
| 207 | 299 | ||
| 208 | var output = ArrayList(u8).init(bot.allocator); | 300 | var output = ArrayList(u8).init(bot.allocator); |
diff --git a/src/types.zig b/src/types.zig index c4c0578..b99d24e 100644 --- a/src/types.zig +++ b/src/types.zig | |||
| @@ -21,6 +21,7 @@ pub const ChatMemberUpdated = @import("types/ChatMemberUpdated.zig"); | |||
| 21 | pub const ChatShared = @import("types/ChatShared.zig"); | 21 | pub const ChatShared = @import("types/ChatShared.zig"); |
| 22 | pub const ChosenInlineResult = @import("types/ChosenInlineResult.zig"); | 22 | pub const ChosenInlineResult = @import("types/ChosenInlineResult.zig"); |
| 23 | pub const Contact = @import("types/Contact.zig"); | 23 | pub const Contact = @import("types/Contact.zig"); |
| 24 | pub const DeleteMessageParams = @import("types/DeleteMessageParams.zig"); | ||
| 24 | pub const Dice = @import("types/Dice.zig"); | 25 | pub const Dice = @import("types/Dice.zig"); |
| 25 | pub const Document = @import("types/Document.zig"); | 26 | pub const Document = @import("types/Document.zig"); |
| 26 | pub const EncryptedCredentials = @import("types/EncryptedCredentials.zig"); | 27 | pub const EncryptedCredentials = @import("types/EncryptedCredentials.zig"); |
diff --git a/src/types/DeleteMessageParams.zig b/src/types/DeleteMessageParams.zig new file mode 100644 index 0000000..9453f83 --- /dev/null +++ b/src/types/DeleteMessageParams.zig | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | // TODO: Integer or String | ||
| 2 | chat_id: i64, | ||
| 3 | message_id: u64, | ||
diff --git a/src/utils.zig b/src/utils.zig index b739f6f..b5d9f19 100644 --- a/src/utils.zig +++ b/src/utils.zig | |||
| @@ -2,8 +2,8 @@ const std = @import("std"); | |||
| 2 | 2 | ||
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | const ArrayList = std.ArrayList; | 4 | const ArrayList = std.ArrayList; |
| 5 | const CaseData = @import("CaseData"); | 5 | const GeneralCategories = @import("GeneralCategories"); |
| 6 | const GenCatData = @import("GenCatData"); | 6 | const LetterCasing = @import("LetterCasing"); |
| 7 | const Utf8View = std.unicode.Utf8View; | 7 | const Utf8View = std.unicode.Utf8View; |
| 8 | 8 | ||
| 9 | pub fn escapeXml(writer: anytype, text: []const u8) !void { | 9 | pub fn escapeXml(writer: anytype, text: []const u8) !void { |
| @@ -18,41 +18,41 @@ pub fn escapeXml(writer: anytype, text: []const u8) !void { | |||
| 18 | } | 18 | } |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | var gcd_global: ?GenCatData = null; | 21 | var gc_global: ?GeneralCategories = null; |
| 22 | 22 | ||
| 23 | pub fn getGCD() !GenCatData { | 23 | pub fn getGC() !GeneralCategories { |
| 24 | if (gcd_global) |gcd| { | 24 | if (gc_global) |gc| { |
| 25 | return gcd; | 25 | return gc; |
| 26 | } | 26 | } |
| 27 | gcd_global = try GenCatData.init(std.heap.page_allocator); | 27 | gc_global = try GeneralCategories.init(std.heap.page_allocator); |
| 28 | return gcd_global.?; | 28 | return gc_global.?; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | var cd_global: ?CaseData = null; | 31 | var lc_global: ?LetterCasing = null; |
| 32 | 32 | ||
| 33 | pub fn getCD() !CaseData { | 33 | pub fn getLetterCasing() !LetterCasing { |
| 34 | if (cd_global) |cd| { | 34 | if (lc_global) |lc| { |
| 35 | return cd; | 35 | return lc; |
| 36 | } | 36 | } |
| 37 | cd_global = try CaseData.init(std.heap.page_allocator); | 37 | lc_global = try LetterCasing.init(std.heap.page_allocator); |
| 38 | return cd_global.?; | 38 | return lc_global.?; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | pub inline fn isNull(value: anytype) bool { | 41 | pub inline fn isNull(value: anytype) bool { |
| 42 | return switch (@typeInfo(@TypeOf(value))) { | 42 | return switch (@typeInfo(@TypeOf(value))) { |
| 43 | .Null => true, | 43 | .null => true, |
| 44 | .Optional => value == null, | 44 | .optional => value == null, |
| 45 | else => false, | 45 | else => false, |
| 46 | }; | 46 | }; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | pub fn isTgWhitespaceStr(str: []const u8) !bool { | 49 | pub fn isTgWhitespaceStr(str: []const u8) !bool { |
| 50 | const view = try Utf8View.init(str); | 50 | const view = try Utf8View.init(str); |
| 51 | const gcd = try getGCD(); | 51 | const gc = try getGC(); |
| 52 | 52 | ||
| 53 | var it = view.iterator(); | 53 | var it = view.iterator(); |
| 54 | while (it.nextCodepoint()) |cp| { | 54 | while (it.nextCodepoint()) |cp| { |
| 55 | if (!isTgWhitespace(gcd, cp)) { | 55 | if (!isTgWhitespace(gc, cp)) { |
| 56 | return false; | 56 | return false; |
| 57 | } | 57 | } |
| 58 | } | 58 | } |
| @@ -60,7 +60,7 @@ pub fn isTgWhitespaceStr(str: []const u8) !bool { | |||
| 60 | return true; | 60 | return true; |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | inline fn isTgWhitespace(gcd: GenCatData, cp: u21) bool { | 63 | inline fn isTgWhitespace(gc: GeneralCategories, cp: u21) bool { |
| 64 | return gcd.isSeparator(cp) or gcd.isControl(cp) or cp == 0x2800 // BRAILLE PATTERN BLANK, telegram treats messages with just this as invalid messages | 64 | return gc.isSeparator(cp) or gc.isControl(cp) or cp == 0x2800 // BRAILLE PATTERN BLANK, telegram treats messages with just this as invalid messages |
| 65 | ; | 65 | ; |
| 66 | } | 66 | } |