diff --git a/Sources/SwiftDEBot/Command/Message Commands/Summarize.swift b/Sources/SwiftDEBot/Command/Message Commands/Summarize.swift new file mode 100644 index 0000000..02e1e36 --- /dev/null +++ b/Sources/SwiftDEBot/Command/Message Commands/Summarize.swift @@ -0,0 +1,73 @@ +import Foundation +import DiscordBM + +struct SummarizeCommand: MessageCommand { + let helpText = "`!summarize`: Fasse einen Link im Reply zusammen." + + func run(client: DiscordClient, message: Gateway.MessageCreate) async throws { + guard message.content == "!summarize" else { return } + + guard let replyContent = message.referenced_message?.value.content else { + try await client.send( + "Schicke bitte `!summarize` als Reply auf eine Nachricht mit einem Link.", + to: message.channel_id + ) + return + } + guard let url = replyContent.firstURL else { + try await client.send( + "In der referenzierten Nachricht sehe ich leider keine URL 🤨", + to: message.channel_id + ) + return + } + log.info("Summarizing \(url)") + + try await client.setTyping(in: message.channel_id) + + guard let encodedURL = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { + log.error("Unable to URL encode \(url)") + return + } + + guard let apiToken = ProcessInfo.processInfo.environment["KAGI_API_TOKEN"] else { + log.error("Necessary env var not found, please set KAGI_API_TOKEN.") + return + } + + let response = try await httpClient.get( + "https://kagi.com/api/v0/summarize?target_language=DE&url=\(encodedURL)", + headers: ["Authorization": "Bot \(apiToken)"], + response: KagiResponse.self + ) + + let summary = response.data.output + + guard !summary.isEmpty else { + try await client.send( + "Das kann ich leider nicht zusammenfassen 🫥", + to: message.channel_id + ) + return + } + + try await client.send(summary, to: message.channel_id) + } +} + +private extension String { + var firstURL: String? { + if let found = self.firstMatch(of: #/(https?://\S+)/#) { + return String(found.output.1) + } + return nil + } +} + + private struct KagiResponse: Decodable { + let data: ResponseData + + struct ResponseData: Decodable { + let output: String + } + } diff --git a/Sources/SwiftDEBot/Commands.swift b/Sources/SwiftDEBot/Commands.swift index acf0cc8..023e7d7 100644 --- a/Sources/SwiftDEBot/Commands.swift +++ b/Sources/SwiftDEBot/Commands.swift @@ -6,6 +6,7 @@ let messageCommands: [MessageCommand] = [ SwiftEvolutionCommand(), AppleStatusCommand(), SPICommand(), + SummarizeCommand(), CowsCommand(), diff --git a/Sources/SwiftDEBot/Utils/HTTPClient+get.swift b/Sources/SwiftDEBot/Utils/HTTPClient+get.swift index e895c21..3c4f6d6 100644 --- a/Sources/SwiftDEBot/Utils/HTTPClient+get.swift +++ b/Sources/SwiftDEBot/Utils/HTTPClient+get.swift @@ -2,8 +2,15 @@ import AsyncHTTPClient import Foundation extension HTTPClient { - func get(_ url: String, response: Response.Type) async throws -> Response where Response: Decodable { - let request = HTTPClientRequest(url: url) + func get( + _ url: String, + headers: [String: String]? = nil, + response: Response.Type + ) async throws -> Response where Response: Decodable { + var request = HTTPClientRequest(url: url) + if let headers { + request.headers = .init(headers.map{ ($0.key, $0.value) }) + } let response = try await self.execute(request, timeout: .seconds(30)) if response.status == .ok { let body = try await response.body.collect(upTo: 50 * 1024 * 1024) // 50 MB