feat: wolfram alpha
This commit is contained in:
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/configuration/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
||||
.env
|
||||
267
Package.resolved
Normal file
267
Package.resolved
Normal file
@@ -0,0 +1,267 @@
|
||||
{
|
||||
"originHash" : "b3f560c4f196109910c6c326162b62c9d4c171d5ba1f22725daa5f5a9ffd2c75",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "async-http-client",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swift-server/async-http-client.git",
|
||||
"state" : {
|
||||
"revision" : "2fc4652fb4689eb24af10e55cabaa61d8ba774fd",
|
||||
"version" : "1.32.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "compress-nio",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/adam-fowler/compress-nio.git",
|
||||
"state" : {
|
||||
"revision" : "e1caa19077dda4b00441142ef57da3db02acd466",
|
||||
"version" : "1.4.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "discordbm",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/DiscordBM/DiscordBM.git",
|
||||
"state" : {
|
||||
"revision" : "90d98d45ae3ee8ed8a2d3d6d86be4aa8ab56515c",
|
||||
"version" : "1.16.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "multipart-kit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vapor/multipart-kit.git",
|
||||
"state" : {
|
||||
"revision" : "3498e60218e6003894ff95192d756e238c01f44e",
|
||||
"version" : "4.7.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-algorithms",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-algorithms.git",
|
||||
"state" : {
|
||||
"revision" : "87e50f483c54e6efd60e885f7f5aa946cee68023",
|
||||
"version" : "1.2.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-asn1",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-asn1.git",
|
||||
"state" : {
|
||||
"revision" : "810496cf121e525d660cd0ea89a758740476b85f",
|
||||
"version" : "1.5.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-async-algorithms",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-async-algorithms.git",
|
||||
"state" : {
|
||||
"revision" : "2971dd5d9f6e0515664b01044826bcea16e59fac",
|
||||
"version" : "1.1.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-atomics",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-atomics.git",
|
||||
"state" : {
|
||||
"revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7",
|
||||
"version" : "1.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-certificates",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-certificates.git",
|
||||
"state" : {
|
||||
"revision" : "24ccdeeeed4dfaae7955fcac9dbf5489ed4f1a25",
|
||||
"version" : "1.18.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-collections",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-collections.git",
|
||||
"state" : {
|
||||
"revision" : "7b847a3b7008b2dc2f47ca3110d8c782fb2e5c7e",
|
||||
"version" : "1.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-configuration",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-configuration.git",
|
||||
"state" : {
|
||||
"revision" : "1bb939fe7bbb00b8f8bab664cc90020c035c08d9",
|
||||
"version" : "1.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-crypto",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-crypto.git",
|
||||
"state" : {
|
||||
"revision" : "6f70fa9eab24c1fd982af18c281c4525d05e3095",
|
||||
"version" : "4.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-distributed-tracing",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-distributed-tracing.git",
|
||||
"state" : {
|
||||
"revision" : "e109d8b5308d0e05201d9a1dd1c475446a946a11",
|
||||
"version" : "1.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-http-structured-headers",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-http-structured-headers.git",
|
||||
"state" : {
|
||||
"revision" : "76d7627bd88b47bf5a0f8497dd244885960dde0b",
|
||||
"version" : "1.6.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-http-types",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-http-types.git",
|
||||
"state" : {
|
||||
"revision" : "45eb0224913ea070ec4fba17291b9e7ecf4749ca",
|
||||
"version" : "1.5.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-log",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-log.git",
|
||||
"state" : {
|
||||
"revision" : "bbd81b6725ae874c69e9b8c8804d462356b55523",
|
||||
"version" : "1.10.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio.git",
|
||||
"state" : {
|
||||
"revision" : "e932d3c4d8f77433c8f7093b5ebcbf91463948a0",
|
||||
"version" : "2.95.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-extras",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-extras.git",
|
||||
"state" : {
|
||||
"revision" : "3df009d563dc9f21a5c85b33d8c2e34d2e4f8c3b",
|
||||
"version" : "1.32.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-http2",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-http2.git",
|
||||
"state" : {
|
||||
"revision" : "b6571f3db40799df5a7fc0e92c399aa71c883edd",
|
||||
"version" : "1.40.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-ssl",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-ssl.git",
|
||||
"state" : {
|
||||
"revision" : "173cc69a058623525a58ae6710e2f5727c663793",
|
||||
"version" : "2.36.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-nio-transport-services",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-nio-transport-services.git",
|
||||
"state" : {
|
||||
"revision" : "60c3e187154421171721c1a38e800b390680fb5d",
|
||||
"version" : "1.26.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-numerics",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-numerics.git",
|
||||
"state" : {
|
||||
"revision" : "0c0290ff6b24942dadb83a929ffaaa1481df04a2",
|
||||
"version" : "1.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-service-context",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-service-context.git",
|
||||
"state" : {
|
||||
"revision" : "d0997351b0c7779017f88e7a93bc30a1878d7f29",
|
||||
"version" : "1.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-service-lifecycle",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swift-server/swift-service-lifecycle",
|
||||
"state" : {
|
||||
"revision" : "89888196dd79c61c50bca9a103d8114f32e1e598",
|
||||
"version" : "2.10.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-syntax",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-syntax.git",
|
||||
"state" : {
|
||||
"revision" : "4799286537280063c85a32f09884cfbca301b1a1",
|
||||
"version" : "602.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-system",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-system.git",
|
||||
"state" : {
|
||||
"revision" : "7c6ad0fc39d0763e0b699210e4124afd5041c5df",
|
||||
"version" : "1.6.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-websocket",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/hummingbird-project/swift-websocket.git",
|
||||
"state" : {
|
||||
"revision" : "ca48d46c25f8fa948d37eaa480c73172182cf90f",
|
||||
"version" : "1.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "xmlcoder",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/CoreOffice/XMLCoder.git",
|
||||
"state" : {
|
||||
"revision" : "5e1ada828d2618ecb79c974e03f79c8f4df90b71",
|
||||
"version" : "0.18.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "zstd",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/facebook/zstd.git",
|
||||
"state" : {
|
||||
"revision" : "f8745da6ff1ad1e7bab384bd1f9d742439278e99",
|
||||
"version" : "1.5.7"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
25
Package.swift
Normal file
25
Package.swift
Normal file
@@ -0,0 +1,25 @@
|
||||
// swift-tools-version: 6.2
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "zundamon",
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/DiscordBM/DiscordBM.git", from: "1.16.0"),
|
||||
.package(url: "https://github.com/thebarndog/swift-dotenv.git", from: "2.1.0"),
|
||||
.package(url: "https://github.com/CoreOffice/XMLCoder.git", from: "0.18.0")
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package, defining a module or a test suite.
|
||||
// Targets can depend on other targets in this package and products from dependencies.
|
||||
.executableTarget(
|
||||
name: "zundamon",
|
||||
dependencies: [
|
||||
.product(name: "DiscordBM", package: "discordbm"),
|
||||
.product(name: "SwiftDotenv", package: "swift-dotenv"),
|
||||
.product(name: "XMLCoder", package: "xmlcoder")
|
||||
],
|
||||
),
|
||||
]
|
||||
)
|
||||
46
Sources/zundamon/Mathsumoto.swift
Normal file
46
Sources/zundamon/Mathsumoto.swift
Normal file
@@ -0,0 +1,46 @@
|
||||
import Foundation
|
||||
import DiscordBM
|
||||
import SwiftDotenv
|
||||
|
||||
@main
|
||||
struct Mathsumoto {
|
||||
nonisolated(unsafe) static private(set) var ownID: UserSnowflake? = nil
|
||||
|
||||
static func main() async throws {
|
||||
let tmp = Result { try Dotenv.configure() }
|
||||
switch (tmp) {
|
||||
case .success:
|
||||
print("Loaded .env file")
|
||||
case .failure:
|
||||
break
|
||||
}
|
||||
|
||||
let token = ProcessInfo.processInfo.environment["DISCORD_TOKEN"]!
|
||||
|
||||
let bot = await BotGatewayManager(token: token, intents: [.guildMessages, .messageContent])
|
||||
|
||||
await withTaskGroup(of: Void.self) { taskGroup in
|
||||
taskGroup.addTask {
|
||||
await bot.connect()
|
||||
let tmp = try! await bot.client.getOwnUser()
|
||||
ownID = try! tmp.decode().id
|
||||
}
|
||||
|
||||
for await event in await bot.events {
|
||||
taskGroup.addTask {
|
||||
await EventHandler(event: event, client: bot.client).handleAsync()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct EventHandler: GatewayEventHandler {
|
||||
let event: Gateway.Event
|
||||
let client: any DiscordClient
|
||||
|
||||
func onMessageCreate(_ payload: Gateway.MessageCreate) async throws {
|
||||
try await MessageHandler(ctx: payload, client: client).handle()
|
||||
}
|
||||
|
||||
}
|
||||
111
Sources/zundamon/MessageHandler.swift
Normal file
111
Sources/zundamon/MessageHandler.swift
Normal file
@@ -0,0 +1,111 @@
|
||||
import Foundation
|
||||
import FoundationNetworking
|
||||
import DiscordBM
|
||||
|
||||
struct MessageHandler {
|
||||
let ctx: Gateway.MessageCreate
|
||||
let client: any DiscordClient
|
||||
|
||||
static let prefix = ":"
|
||||
static let zundaGifData = try? Data(contentsOf: URL(filePath: "media/zundamone.gif"))
|
||||
|
||||
func handle() async throws {
|
||||
guard !(ctx.author?.bot ?? false) else { return }
|
||||
if (ctx.content.hasPrefix(MessageHandler.prefix)) {
|
||||
let split = ctx.content.split(separator: " ")
|
||||
let command = split.first?.trimmingPrefix(MessageHandler.prefix)
|
||||
let args = split[1...]
|
||||
|
||||
switch command {
|
||||
case "wow": try await handleWow(args)
|
||||
case "domath": try await Wolfram.handleMath(args, client: client, ctx: ctx)
|
||||
default: break
|
||||
}
|
||||
} else if ctx.mentions.contains(where: { $0.id == Mathsumoto.ownID }) {
|
||||
if ctx.content
|
||||
.replacingOccurrences(of: "<@\(Mathsumoto.ownID!.rawValue)>", with: "")
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.count == 0,
|
||||
let zundaGif = MessageHandler.zundaGifData
|
||||
{
|
||||
try await client.createMessage(
|
||||
channelId: ctx.channel_id,
|
||||
payload: .init(
|
||||
message_reference: .init(
|
||||
type: .default,
|
||||
message_id: ctx.id,
|
||||
channel_id: ctx.channel_id,
|
||||
guild_id: ctx.guild_id,
|
||||
),
|
||||
files: [.init(data: .init(data: zundaGif), filename: "zundamone.gif")],
|
||||
attachments: [.init(index: 0, filename: "zundamone.gif")],
|
||||
)
|
||||
).guardSuccess()
|
||||
} else {
|
||||
try await handle8Ball()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static let ballResponses = [
|
||||
"It is certain.",
|
||||
"It is decidedly so.",
|
||||
"Without a doubt.",
|
||||
"Yes – definitely.",
|
||||
"You may rely on it.",
|
||||
"As I see it, yes.",
|
||||
"Most likely.",
|
||||
"Outlook good.",
|
||||
"Yes.",
|
||||
"Signs point to yes.",
|
||||
"Reply hazy, try again.",
|
||||
"Ask again later.",
|
||||
"Better not tell you now.",
|
||||
"Cannot predict now.",
|
||||
"Concentrate and ask again.",
|
||||
"Don’t count on it.",
|
||||
"My reply is no.",
|
||||
"My sources say no.",
|
||||
"Outlook not so good.",
|
||||
"Very doubtful.",
|
||||
"Ui beam",
|
||||
"We are Shigure Ui",
|
||||
"We are Shigure Ux",
|
||||
]
|
||||
func handle8Ball() async throws {
|
||||
try await client.createMessage(
|
||||
channelId: ctx.channel_id,
|
||||
payload: .init(
|
||||
content: MessageHandler.ballResponses.randomElement(),
|
||||
message_reference: .init(
|
||||
type: .default,
|
||||
message_id: ctx.id,
|
||||
channel_id: ctx.channel_id,
|
||||
guild_id: ctx.guild_id,
|
||||
),
|
||||
)
|
||||
).guardSuccess()
|
||||
}
|
||||
|
||||
static let wows = [
|
||||
"<:wow:1477062414913634334>",
|
||||
"<:wow2:1477062432357875948>",
|
||||
"<:wow4:1477062471746588713>",
|
||||
"<:wow5:1477062452804849845>"
|
||||
]
|
||||
func handleWow(_ args: ArraySlice<String.SubSequence>) async throws {
|
||||
try await client.createMessage(
|
||||
channelId: ctx.channel_id,
|
||||
payload: .init(
|
||||
content: MessageHandler.wows.randomElement(),
|
||||
message_reference: .init(
|
||||
type: .default,
|
||||
message_id: ctx.id,
|
||||
channel_id: ctx.channel_id,
|
||||
guild_id: ctx.guild_id,
|
||||
),
|
||||
)
|
||||
).guardSuccess()
|
||||
}
|
||||
|
||||
}
|
||||
105
Sources/zundamon/Wolfram.swift
Normal file
105
Sources/zundamon/Wolfram.swift
Normal file
@@ -0,0 +1,105 @@
|
||||
import Foundation
|
||||
import FoundationNetworking
|
||||
import XMLCoder
|
||||
import DiscordBM
|
||||
|
||||
struct Wolfram {
|
||||
static let token = ProcessInfo.processInfo.environment["WOLFRAM_APP_ID"]!
|
||||
static let apiUrl = "http://api.wolframalpha.com/v2/query"
|
||||
|
||||
static func getWolfram(_ question: String) async throws -> (String, URL?) {
|
||||
let encQuestion = question.addingPercentEncoding(withAllowedCharacters: .alphanumerics)
|
||||
let url = URL(string: "\(apiUrl)?appid=\(token)&input=\(encQuestion!)")!
|
||||
let (data, _) = try await URLSession.shared.data(from: url)
|
||||
|
||||
let wolframRes = try XMLDecoder().decode(WolframQueryResult.self, from: data)
|
||||
|
||||
let resultPod = wolframRes.pod.first(where: { $0.primary ?? false || $0.id == "Result" })
|
||||
|
||||
var ans: String?
|
||||
var img: URL?
|
||||
|
||||
if let resultPod = resultPod {
|
||||
ans = String(resultPod.subpod.compactMap(\.plaintext).joined(by: "\n"))
|
||||
.replacingOccurrences(of: " | ", with: ": ")
|
||||
}
|
||||
if let imgPod = wolframRes.pod.first(where: {
|
||||
["RootPlot", "NumberLine", "Plot", "ImplicitPlot", "3DPlot"].contains($0.id) ||
|
||||
($0.id == "Example" && $0.scanner == "Dice")
|
||||
}) {
|
||||
ans = ans ?? "Plot:"
|
||||
img = imgPod.subpod.first?.img.src
|
||||
}
|
||||
|
||||
if ans == nil,
|
||||
let maybePod = wolframRes.pod.first(where: { $0.title == "Input interpretation" }),
|
||||
let maybeText = maybePod.subpod.first?.plaintext
|
||||
.replacingOccurrences(of: " | ", with: ": ") {
|
||||
ans = "<:smol_rise:852763040452575252> I don't know. Maybe you meant '\(maybeText)'"
|
||||
}
|
||||
|
||||
return (ans ?? "<:smol_rise:852763040452575252> sorry, I have no idea (´._.`)", img)
|
||||
}
|
||||
|
||||
static func handleMath(
|
||||
_ args: ArraySlice<String.SubSequence>,
|
||||
client: DiscordClient,
|
||||
ctx: Gateway.MessageCreate
|
||||
) async throws {
|
||||
try await client.triggerTypingIndicator(channelId: ctx.channel_id).guardSuccess()
|
||||
|
||||
let question = String(args.joined(by: " "))
|
||||
|
||||
let (answer, img) = try await getWolfram(question)
|
||||
|
||||
var attachments: [Payloads.Attachment] = []
|
||||
var files: [RawFile] = []
|
||||
|
||||
if let img = img {
|
||||
let data = try? await URLSession.shared.data(from: img)
|
||||
if let data = data?.0 {
|
||||
attachments.append(.init(index: 0, filename: "img.gif"))
|
||||
files.append(.init(data: .init(data: data), filename: "img.gif"))
|
||||
}
|
||||
}
|
||||
|
||||
try await client.createMessage(channelId: ctx.channel_id, payload: .init(
|
||||
content: answer,
|
||||
message_reference: .init(
|
||||
type: .default,
|
||||
message_id: ctx.id,
|
||||
channel_id: ctx.channel_id,
|
||||
guild_id: ctx.guild_id,
|
||||
),
|
||||
files: files,
|
||||
attachments: attachments
|
||||
)).guardSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
struct WolframQueryResult: Codable {
|
||||
struct Pod: Codable {
|
||||
let title: String
|
||||
let id: String
|
||||
let scanner: String
|
||||
let primary: Bool?
|
||||
|
||||
let subpod: [Subpod]
|
||||
}
|
||||
|
||||
struct Subpod: Codable {
|
||||
let title: String
|
||||
let img: Image
|
||||
let plaintext: String
|
||||
}
|
||||
|
||||
struct Image: Codable {
|
||||
let src: URL
|
||||
}
|
||||
|
||||
let success: Bool
|
||||
let numpods: Int
|
||||
|
||||
let pod: [Pod]
|
||||
}
|
||||
|
||||
BIN
media/zundamone.gif
Normal file
BIN
media/zundamone.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 MiB |
Reference in New Issue
Block a user