8000 GitHub - DiscordBM/DiscordBM at v1.0.0-beta.20
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

DiscordBM/DiscordBM

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DiscordBM

Tests Badge Minimum Swift Version

Notable Features

  • Everything with structured concurrency and async/await. Full integration with all the latest Server-Side Swift packages.
  • Connect to the Discord gateway and receive all events easily.
  • Send requests to the Discord API using library's Discord client.
  • Hard-typed APIs. All Gateway events have their own type and all Discord API responses can be decoded easily.
  • Abstractions for easier testability.

Showcase

You can see Vapor community's Penny bot as a showcase of using this library in production. Penny is used to give coins to the helpful members of the Vapor community as a sign of appreciation.
Penny is available here and you can find DiscordBM used in the PennyBOT target.

How To Use

If you're using DiscordBM on macOS Ventura (on either Xcode or VSCode), make sure you have Xcode 14.1 or above. Lower Xcode 14 versions have known issues that cause a lot of problems for libraries.

First you need to initialize a BotGatewayManager instance, then tell it to connect and start using it:

Initializing a Gateway Manager On Your Own

Make sure you've added AsyncHTTPClient to your dependancies.

import DiscordBM
import AsyncHTTPClient

let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let bot = BotGatewayManager(
    eventLoopGroup: httpClient.eventLoopGroup,
    httpClient: httpClient,
    token: YOUR_BOT_TOKEN,
    appId: YOUR_APP_ID,
    presence: .init( /// Set up bot's initial presence
        /// Will show up as "Playing Fortnite"
        activities: [.init(name: "Fortnite", type: .game)], 
        status: .online,
        afk: false
    ),
    intents: [.guildMessages, .messageContent] /// Add all the intents you want
)

/// it's important to shutdown the httpClient _after all requests are done_, even if one failed
/// libraries like Vapor take care of this on their own if you use the shared http client
try httpClient.syncShutdown()
/// Prefer to use `shutdown()` in async contexts:
/// try await httpClient.shutdown()

See the GatewayConnection tests or Vapor community's Penny bot for real-world examples.

Initializing a Gateway Manager With Vapor

import DiscordBM
import Vapor

let app: Application = YOUR_VAPOR_APPLICATION
let bot = BotGatewayManager(
    eventLoopGroup: app.eventLoopGroup,
    httpClient: app.http.client.shared,
    token: YOUR_BOT_TOKEN,
    appId: YOUR_APP_ID,
    presence: .init( /// Set up bot's initial presence
        /// Will show up as "Playing Fortnite"
        activities: [.init(name: "Fortnite", type: .game)],
        status: .online,
        afk: false
    ),
    intents: [.guildMessages, .messageContent] /// Add all the intents you want
)

Using The Gateway Manager

import DiscordBM

/// Make an instance like above
let bot: BotGatewayManager = ...

/// Add event handlers
Task {
    await bot.addEventHandler { event in
        switch event.data {
        case let .messageCreate(message):
            print("GOT MESSAGE!", message)
            /// Switch over other cases you have intents for and you care about.
        default: break
        }
    }
    
    /// If you care about library parsing failures, handle them here
    await bot.addEventParseFailureHandler { error, text in
        /// Handle the failure using the `error` thrown and the `text` received.
    }

    /// Tell the manager to connect to Discord
    await bot.connect()

    /// Use `bot.client` to send requests to Discord.
    try await bot.client.createMessage(
        channelId: CHANNEL_ID,
        payload: .init(content: "Hello Everybody!")
    )
}

/// If you don't use libraries like Vapor that do this for you, 
/// you'll need to uncomment this line and call it from a non-async context.
/// Otherwise your executable will exit immediately after every run.
/// RunLoop.current.run()
8000

Sending Attachments

It's usually better to send a link of your media to Discord, instead of sending the actual file.
However, DiscordBM still supports sending files directly.

Task {
    /// Raw data of anything like an image
    let image: ByteBuffer = ...
    
    /// Example 1
    try await bot.client.createMessage(
        channelId: CHANNEL_ID,
        payload: .init(
            content: "A message with an attachment!",
            files: [.init(data: image, filename: "pic.png")],
            attachments: [.init(index: 0, description: "Picture of something secret :)")]
            ///                 ~~~~~~~^ `0` is the index of the attachment in the `files` array.
        )
    )
    
    /// Example 2
    try await bot.client.createMessage(
        channelId: CHANNEL_ID,
        payload: .init(
            embeds: [.init(
                title: "An embed with an attachment!",
                image: .init(url: .attachment(name: "penguin.png"))
                ///                          ~~~~~~~^ `penguin.png` is the name of the attachment in the `files` array.   
            )],
            files: [.init(data: image, filename: "penguin.png")]
        )
     )
}

Take a look at testMultipartPayload() in /Tests/DiscordClientTests to see how you can send media in a real-world situation.

Finding Your Bot Token

In Discord developer portal: Finding Bot Token

Finding Your App ID

In Discord developer portal: Finding App ID

Testability

DiscordBM comes with tools to make testing your app easier.

  • You can type-erase your BotGatewayManagers using the GatewayManager protocol so you can override your gateway manager with a mocked implementation in tests.
  • You can also do the same for DefaultDiscordClient and type-erase it using the DiscordClient protocol so you can provide a mocked implementation when testing.

How To Add DiscordBM To Your Project

To use the DiscordBM library in a SwiftPM project, add the following line to the dependencies in your Package.swift file:

.package(url: "https://github.com/MahdiBM/DiscordBM", from: "1.0.0-beta.1"),

Include "DiscordBM" as a dependency for your targets:

.target(name: "<target>", dependencies: [
    .product(name: "DiscordBM", package: "DiscordBM"),
]),

Finally, add import DiscordBM to your source code.

Versioning

This library will try to follow Semantic Versioning 2.0.0, with exceptions.
These exceptions should not be a big deal depending on your code style, but can result in some slight code breakage.

  • Adding enum cases.
    • This is so DiscordBM can continue to add new cases to public enums in minor versions.
    • If you care about code breakage, you can't use exhaustive switch statements.
      Either include default: in your switch statements, or use if let/if case let.
    • See this for more info.
  • Passing initializers/functions as arguments.
    • This is so DiscordBM can continue to add new parameters to public initializers/functions in minor versions.
    • If you care about code breakage, you can't write code like value.map(SomeDiscordBMType.init).
      Luckily, not many people do or need this anyway.

Contribution & Support

Any contribution is more than welcome. You can find me in Vapor's Discord server to discuss your ideas.
I'm also actively looking for any new info in the Discord API, and will add them to the library as soon as I can.

About

A Multiplatform Swift Discord Library, Primarily For Making Bots

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages

No packages published

Contributors 6

Languages

0