Connect with us

FACEBOOK

Build a Discord Bot with Rust and Serenity

Published

on

By Joe Previte

Discord is on the rise in developer communities. And, as we all know, developers love building on top of platforms they use. It’s fun!

Today, I’m going to show you how to build your own Discord bot using Rust and serenity.

Building the App

Before building the app, I’ll cover how it will work and the prerequisites for following along. After that, I’ll jump in and go through each step before setting it up to run locally on your machine. Finally, I’ll show how to test it out in your own Discord server.

How will it work?

Imagine I’m creating a bot for a developer community Discord server. I’m going to build a Discord bot which supports a single command !help, which will return a message explaining:

  • What channel to post in for technical help
  • Where to find the code of conduct
  • How to get in touch with admins of the server

This could be helpful for new people who need help for various scenarios. Think of it being analogous to the –help flag commonly used by CLIs.

Prerequisites

In order to start writing this Rust application, there are a few requirements:

  • Rust installed locally
  • IDE setup for Rust development
  • Discord account

Install Rust

I already have Rust installed locally. If you don’t, you can install it locally using:

 shell curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 

Afterwards, run rustup –version to verify that it worked. If it did, you should see something printed to your terminal.

IDE for Rust Development

I’m going to be using VS Code and the rust-analyzer extension. You can find support for other IDEs under the Tools on the rust-lang website.

Discord Account

I already have an account, but if you don’t, you can sign up for free.

Set up Code

Since this is a new project, I’m going to create a new project with cargo. For simplicity, I’ll name the project discord-help-bot.

 shell cargo new discord-help-bot 

Add Dependencies

This project requires two external dependencies:

  • tokio – “A runtime for writing reliable, asynchronous and slim applications with the Rust programming language”
  • serenity – “a Rust library for the Discord API”

I’ll add both to the Cargo.toml file:

 toml [dependencies] tokio = { version = "0.2", features = ["macros"] } serenity = { default-features = false, features = ["client", "gateway", "model", "rustls_backend"], version = "0.9.0-rc.1"} 

Note: you’ll notice this is a release candidate (rc). When you go through this, check to make sure you’re using the latest version of serenity.

tokio allows the program to run asynchronously and serenity allows you to interact with the Discord API. With both of these dependencies, I can start adding logic to the program.

Add Logic to main.rs

Since this is a small program, I will only need to add code to the main.rs file. I’ll add the code and then afterwards I’ll walk through how it works.

 rust use std::env; use serenity::{ async_trait, model::{channel::Message, gateway::Ready}, prelude::*, }; const HELP_MESSAGE: &str = " Hello there, Human! You have summoned me. Let's see about getting you what you need. ? Need technical help? => Post in the <#CHANNEL_ID> channel and other humans will assist you. ? Looking for the Code of Conduct? => Here it is: <https://opensource.facebook.com/code-of-conduct> ? Something wrong? => You can flag an admin with @admin I hope that resolves your issue! -- Helpbot "; const HELP_COMMAND: &str = "!help"; struct Handler; #[async_trait] impl EventHandler for Handler { async fn message(&self, ctx: Context, msg: Message) { if msg.content == HELP_COMMAND { if let Err(why) = msg.channel_id.say(&ctx.http, HELP_MESSAGE).await { println!("Error sending message: {:?}", why); } } } async fn ready(&self, _: Context, ready: Ready) { println!("{} is connected!", ready.user.name); } } #[tokio::main] async fn main() { let token = env::var("DISCORD_TOKEN") .expect("Expected a token in the environment"); let mut client = Client::new(&token) .event_handler(Handler) .await .expect("Err creating client"); if let Err(why) = client.start().await { println!("Client error: {:?}", why); } } 

Code Walkthrough

I’m going to walk through the code in four chunks. In the first chunk, I’ll look at the following:

 rust use std::env; use serenity::{ async_trait, model::{channel::Message, gateway::Ready}, prelude::*, }; 

These are the use declarations. They make it easier for developers because they “shorten the path required to refer to a module item.” Here, there are two blocks. The first refers to the env module from the standard library, which we later use to access the DISCORD_TOKEN environment variable.

The next block refers to the modules I use provided by serenity. The first is async_trait which I use on the Handler to tell the compiler the type and methods Handler should support. After that are two structs, Message and Ready. The first is used inside the function signature of message to indicate the type for the third parameter msg. As you can guess, this is for the shape of the message when we receive it from the Discord server. The other struct is Ready and is used in the function signature of the ready function for our Handler. The last line here is the prelude which says, “include the basic things out of the box for the user.”

In the second chunk, I’ll discuss the following piece of code:

 rust const HELP_MESSAGE: &str = " Hello there, Human! You have summoned me. Let's see about getting you what you need. ? Need technical help? => Post in the <#CHANNEL_ID> channel and other humans will assist you. ? Looking for the Code of Conduct? => Here it is: <https://opensource.facebook.com/code-of-conduct> ? Something wrong? => You can flag an admin with @admin I hope that resolves your issue! -- Helpbot "; const HELP_COMMAND: &str = "!help"; 

I declare both HELP_MESSAGE and HELP_COMMAND using the const keyword because they stay constant throughout the lifetime of the program and don’t change. With const, you must explicitly annotate the type. We use the &str because these are string slices.

In the third chunk, I’ll look at the following:

 rust struct Handler; #[async_trait] impl EventHandler for Handler { async fn message(&self, ctx: Context, msg: Message) { if msg.content == HELP_COMMAND { if let Err(why) = msg.channel_id.say(&ctx.http, HELP_MESSAGE).await { println!("Error sending message: {:?}", why); } } } async fn ready(&self, _: Context, ready: Ready) { println!("{} is connected!", ready.user.name); } } 

In this part of the program, I declare struct Handler. This doesn’t do much because all it does is declare the struct without any fields. In the next block, we use the #[async_trait] macro to tell the compiler that the struct below implements that trait like allowing us to use the async keyword with our functions and the .await method.

After that, the impl EventHandler for Handler tells the compiler, “My struct called Handler is going to look like an EventHandler.” Inside the struct are two functions: message and ready. The message function is where the main logic of our program happens. It takes in a message, checks the content to see if it matches the HELP_COMMAND and it does, it sends the HELP_MESSAGE to that channel using the same channel id. If there’s an error, it prints it.

The ready function logs a statement letting us know the handler for our Discord bot is ready using the bot’s name.

Last, the final chunk I have to walk through is:

 rust #[tokio::main] async fn main() { let token = env::var("DISCORD_TOKEN") .expect("Expected a token in the environment"); let mut client = Client::new(&token) .event_handler(Handler) .await .expect("Err creating client"); if let Err(why) = client.start().await { println!("Client error: {:?}", why); } } 

The first thing I see is the #[tokio::main] macro which is used because this is an asynchronous application. The next is the main function which is called when the program runs. The first thing it does is get the DISCORD_TOKEN environment variable. Then, it creates a new Serenity Client using the token for us to talk to the Discord API. Last, the program starts the client and handles the error if it has issues starting up.

And that’s all the logic for the program! Onto the next step.

Create a Discord Server

In order to test this out, I’ll need my own Discord Server. To create one, follow these steps:

  • Open the Discord application
  • On the left side, click the plus icon “Add a Server”
  • Click “Create a server”
  • Once you’ve filled everything out, click “Create”

Create Channel and Get Channel ID

Since I’m starting from scratch, I’ll need to create a new channel. I can do this by clicking the plus icon on the left next to “TEXT CHANNELS”. I’ll call mine “help”.

I need to get the channel ID. Discord makes it easy to expose this in the UI if you toggle on Developer Mode. To get here, go to Preferences > Appearance > Developer Mode.

To see the channel ID, right click on the channel and select “copy ID”.

Returning to the application, replace the “CHANNEL_ID” placeholder text with yours. After words, it should look something like `<#750828544917110936>`. Make sure you have the angled brackets and the “#” symbol.

Add a Role

I’ll also need to add a role. To do this, follow these steps:

  • In the top left, click on the server dropdown menu
  • Select “Server Settings”
  • Go to Roles on the left
  • Click the plus next to Roles in the middle
  • Type in the role name – I’m using the name “admin”
  • Click “Save changes”

To make yourself that role, right click on your Discord handle either in a message on the right sidebar, select Roles > admin.

Now that I have the role set up, someone can actually use the @admin to get my attention.

Create Discord Application

In order to create a Discord bot, I first need to create a Discord application. Follow these steps to do this:

After this is done, I can move on to the next step to create a bot.

Create Discord Bot and Install on Discord Server

The next step is to create a Discord Bot. Think of this as the profile for the bot. To create one, do the following:

  • On the left sidebar, click “Bot”
  • Click “Add Bot”
  • Feel free to change the name and the icon here
  • After you’re done, click “OAuth2” on the left sidebar
  • Under scopes, select “bot”
  • Scroll down to permissions and select “Send Messages” and “Read Message History”
  • Scroll up and click “copy” to copy the generated OAuth url
  • Paste it in a new tab in your browser
  • It will ask you which server you want to add it to. Note: you can only add bots to servers where you have the “Manage Server” permissions. Since we created our own, we do. Add it to the server you created earlier.
  • Click “Continue”
  • Confirm that you want to allow the bot to send messages and click “Authorize”

Great! Now the Discord bot is created.

Before I leave the Discord Developer Portal, I need one last thing: the Discord Bot auth token. To see it, follow these steps:

  • Click “Bot” on the left sidebar again
  • Next to the bot icon, look for the token
  • Click “copy”

I’m going to save this now because I’ll need it later when I run the application locally. Also, a friendly reminder to not commit this to git. You don’t want your auth token to get in the wrong hands!

Build Code

I am ready to build my project for testing locally. To this, I can run:

 shell cargo build 

If the compiler is happy with my code, it will output my code under /target/debug/ and contain an executable using the name key in the Cargo.toml. In my case, this is discord-help-bot.

Run Locally

It’s time to run the executable locally and see if my bot starts up as expected. I will run the command:

 shell DISCORD_TOKEN=<use your token here> ./target/debug/discord-help-bot 

I’ll replace “<use your token here>” with my token from earlier. This makes the token available as an environment variable to the application.

If it worked, you should see a message printed to the terminal saying “BotName is connected!” where “BotName” shows the name of your bot according to what you named it.

Test Bot on Discord Server

The moment I’ve been waiting for – testing my bot on my Discord server. With the bot still running from the previous step, I will open my Discord server where the bot is installed and send the message “!help” in the #general channel. And it works! I see the help message I wrote earlier posted to the channel almost immediately by my bot.

Woohoo! Mission accomplished.

Summary

Congratulations on making it through this project with me! I had a lot of fun and hope you did as well. I built a basic Discord bot with Rust and tested it on my Discord server by running it locally.

If you’d like to see a full-working version of this, you can check it out here on GitHub. If you want to make this better, I encourage you to open a pull request! You can ask questions there in an issue or open an issue if you find a bug. All contributions are welcome!

Next Steps

If you’d like to continue hacking on this project further, here are a few ideas:

  • Print a message to the console along with the username of the person who interacted with the bot
  • Add an avatar/icon to the bot
  • Add a second command to the bot
  • Deploy the bot

Let me know if you do this or something similar. We’d love to see what you build.

Resources for Learning More Rust

Looking for more ways to develop your Rust skills? Here are a few recommendations for continuing your learning:

Thanks for reading. Happy coding!

Special thanks to the maintainers of Serenity for the project examples and Will Harris’ article “How to Add a Bot to Discord” both of which made writing this tutorial possible.

To learn more about Facebook Open Source, visit our open source site, subscribe on Youtube, or follow us on Twitter and Facebook.

Facebook Developers

Continue Reading

FACEBOOK

Messenger API to support Instagram

Published

on

Today, we are announcing updates to the Messenger API to support Instagram messaging, giving businesses new tools to manage their customer communications on Instagram at scale. The new API features enable businesses to integrate Instagram messaging with their preferred business applications and workflows; helping drive more meaningful conversations, increase customer satisfaction and grow sales. The updated API is currently in beta with a limited number of developer partners and businesses.

Instagram is a place for emerging culture and trend creation and discovering new brands is a valuable part of this experience. Messaging plays a central role in helping people connect with brands in personal ways through story replies, direct messages, and mentions. Over the last year, total daily conversations between people and businesses on Messenger and Instagram grew over 40 percent. For businesses, the opportunity to drive sales and improve customer satisfaction by having meaningful interactions with people on Instagram messaging is huge.

Instagram is a platform for community building, and we’ve long approached it as a way for us to connect with our customers in a place where they are already spending a lot of their time. With the newly launched Messenger API support for Instagram, we are now able to increase efficiency, drive even stronger user engagement, and easily maintain a two-way dialogue with our followers. This technology has helped us create a new pipeline for best-in-class service and allows for a direct line of communication that’s fast and easy for both customers and our internal team.” – Michael Kors Marketing

Works with your tools and workflows

Businesses want to use a single platform to respond to messages on multiple channels. The Messenger API now allows businesses to manage messages initiated by people throughout their Instagram presence, including Profile, Shops, and Stories. It will be possible for businesses to use information from core business systems right alongside Instagram messaging, enabling more personal conversations that drive better business outcomes. For example, businesses integrating with a CRM system can give agents a holistic view of customer loyalty. Furthermore, existing investments in people, tools, and workflows to manage other communication channels can be leveraged and extended to support customers on Instagram. This update will also bring Facebook Shops messaging features to the Messenger API so businesses can create more engaging and connected customer experiences.

To get started, businesses can easily work with developers to integrate Instagram messaging with their existing tools and systems.

Increases responsiveness and customer satisfaction

Customers value responsiveness when they have questions or need help from businesses. For the first time on Instagram, we’re introducing new features that will allow businesses to respond immediately to common inquiries using automation, while ensuring people are seamlessly connected to live support for more complex inquiries. One of our alpha partners, Clarabridge, reported their client brands had improved their response rate by up to 55% since being able to manage Instagram DMs through their platform.

The updates to the Messenger API are part of our overall effort to make it easier for businesses to reach their customers across our family of apps.

Messenger API support for Instagram is currently in beta with a focus on providing high quality, personalized messaging experiences on Instagram while increasing business efficiency. Adidas, Amaro, Glossier, H&M, MagazineLuiza, Michael Kors, Nars, Sephora and TechStyle Fashion Group and other consumer brands are already participating in the beta program. We are excited about early results some businesses saw during alpha testing, including higher response rates, reduced resolution times, and deeper customer insights as a result of integrations. We’re also testing with a limited number of developer partners; and are delighted at the initial response.

On average, brands have saved at least four hours per agent per week by streamlining social community management within the Khoros platform, plus shortened response rates during business hours — which is crucial to meet as customers who message brands on social media expect a quick reply.” – Khoros

If you are a business interested in utilizing the API post-beta, please sign up for our business waitlist. If you are a developer, please sign up for our developer waitlist.

Facebook Developers

Continue Reading

FACEBOOK

Required migration to token-based access for User Picture and oEmbed endpoints

Published

on

As part of our Graph API 8.0 release, we announced breaking changes to how developers can access certain permissions and APIs. Starting October 24, 2020, developers need to migrate to token-based access in order to access User Picture and oEmbed endpoints for Facebook and Instagram.

This post outlines these changes and the necessary steps developers need to take to avoid disruption to their app.

User Picture

Facebook will now require client or app access tokens to access a user’s profile picture. Beginning on October 24, 2020 queries for profile pictures made against user IDs without an access token will return a generic silhouette rather than a profile picture. This is a breaking change for partners. While client or app tokens will be required for user ID queries, they will continue to be a best practice (and not required) for ASID queries for the time being.

Facebook and Instagram oEmbed

We are also deprecating the existing Legacy API oEmbed endpoints for Facebook and Instagram on October 24, 2020, which will be replaced with new Graph API endpoints. If developers don’t make this change and continue to attempt to call the existing oEmbed API, their requests will fail and developers will receive an error message instead. These new endpoints will require client or app access tokens or ASID queries.

Ready to make the switch? You can read more about these changes in our developer documentation for User Picture and also visit our changelog for Facebook OEmbed and IG OEmbed for details on how to start calling these Graph API endpoints.

Facebook Developers

Continue Reading

FACEBOOK

PyTorch Tutorials Refresh – Behind the Scenes

Published

on

Hi, I’m Jessica, a Developer Advocate on the Facebook Open Source team. In this blog, I’ll take you behind the scenes to show you how Facebook supports and sustains our open source products – specifically PyTorch, an open source deep learning library. With every new release version, PyTorch pushes out new features, updates existing ones, and adds documentation and tutorials that cover how to implement these new changes.

On May 5, 2020, PyTorch released improvements to Tutorials homepage with new content and a fresh usability experience out into the world (see the Twitter thread) for the community. We introduced keyword based search tags and a new recipes format (bite-sized, ready-to-deploy examples) and more clearly highlighted helpful resources, which resulted in the fresh homepage style you see today.

Something Went Wrong

We’re having trouble playing this video.

As the framework grows with each release, we’re continuously collaborating with our community to not only create more learning content, but also make learning the content easier.

What Changed

The tutorials refresh project focused on re-envisioning the learning experience by updating the UX and updating the learning content itself.

Our 3 major goals for the refresh were:

  • Reduce blocks of text and make it easy for users to find important resources (e.g. PyTorch Cheat Sheet, New to PyTorch tutorials)
  • Improve discoverability of relevant tutorials and surface more information for users to know about the available tutorial content
  • Create content that allows users to quickly learn and deploy commonly used code snippets

And we addressed these goals by:

  • Adding callout blocks with direct links to highlight important resource such as the beginner tutorial, the PyTorch Cheat Sheet and new recipes
  • Adding filterable tags to help users easily find relevant tutorials; and formatting the tutorials cards with summaries so users know what to expect without having to click in
  • Creating a new learning format, Recipes, and 15 brand new recipes covering some of the most popular PyTorch topics such as interpretability and quantization as well as basics such as how to load data in PyTorch
  • In summary:

Goals

Implementation

Reduce blocks of text and make it easy for users to find important resources (e.g. PyTorch Cheat Sheet, New to PyTorch tutorials)

Add callouts with direct links to highlight important resources

Improve discoverability of relevant tutorials and surface more information for users to know about the available tutorial content

Add filterable tags to help users easily find relevant tutorials. Reformat tutorial cards with summaries so users know what to expect

Create content that allow users to quickly learn and deploy commonly used code snippets

Create a new learning format – Recipes. These are bite-sized, actionable examples of how to use specific Pytorch features, different from our previous full-length tutorials

Why We Made These Changes

Now, what drove these changes? These efforts were driven by feedback from the community; two sources of feedback were the UX research study and direct community interactions:

  • UX Research study – Earlier in 2020, we conducted a UX research study of our website in collaboration with the Facebook UX Research team to understand how our developer community is using the website and evaluate ways we can improve it to better meet their needs
  • In-person events and online feedback – The team gathered community feedback about existing tutorials to help identify learning gaps.

We used these channels of input to fuel revisioning our learning experience.

Rethinking the Learning Experience

Given the feedback from the UX Research study and the in-person workshop, we went back and rethought the current learning experience.

  • 3 levels
    PyTorch Tutorials Refresh - Behind the Scenes
    • Level 1: API docs. Currently these exist and they contain code snippets that provide an easily understandable (and reproducible) example that allows a user to implement that particular API
    • Level 2: The missing puzzle piece
    • Level 3: Tutorials ideally provide an end-to-end experience that provides users an understanding of how to take data, train a model and deploy it into a production setting using PyTorch. These exist, but needed to be pruned of outdated content and cleaned up to better fit this model
    • Realized we were missing something in between, content that was short, informative, and actionable. That’s how recipes were born. Level 2: Recipes are bite-sized, actionable examples of how to use specific PyTorch features, different from our tutorials

What Was the Process

Just as it took a large team effort, this was more of a marathon as opposed to a sprint. Let’s look at the process:

Timeline of the process:

PyTorch Tutorials Refresh - Behind the Scenes

Overall, the project took about 6 months, not including the UX research and prior feedback collection time. It started off with the kickoff discussion to align on the changes. We assessed the existing tutorials, pruned outdated content and decided on new recipe topics and assigned authors. In the meantime, marketing and documentation engineers collaborated with our web design team on the upcoming UI needs, created mocks to preview with the rest of the team and built out the infrastructure.

For logistics, we created a roadmap and set milestones for the team of authors. We held weekly standup meetings, and the team bounced ideas in chat. The changes were all made in a staging branch in GitHub, which allowed us to create previews of the final build. Next, the build process. Many of the recipe authors were first time content creators, so we held a live onboarding session where we discussed teaching mindset, writing with an active voice, outlining, code standards and requirements; and this was all captured in a new set of content creation documentation.

The bulk of the process was spent in building out the content, copy editing and implementing the UI experience.

With the product out the door, we took some time to perform a team retrospective – asking what went well? What went poorly? What can we do better next time? In addition, we continue to gather ongoing feedback from the community through GitHub issues.

Moving forward, we are brainstorming and forming a longer-term plan for the PyTorch learning experience as it relates to docs and tutorials.

Ways to Improve

Looking back on ways we could have improved:

  • Timeline – Our timeline ended up taking longer than anticipated because it had been coupled with a previous version release and team members were serving double-duty working on release content, as well as tutorials refresh content. As version release approached, we took a step back and realized we needed more time to put out a more polished product.
  • Testing – In software development, if there is an impending deadline, typically testing is the first thing to get squished; however, more focused testing will always save time in the bigger picture. For us, we would always welcome more time for more CI tests of the tutorial build, as well as beta tests of the user experience. Both of these are ongoing works in progress, as we continue to improve the tutorials experience overall.

What’s Next

So what’s next? We understand that this was just one change in a larger landscape of the overall PyTorch learning experience, but we are excited to keep improving this experience for you, our dedicated PyTorch user.

We would like to hear from you about your experience in the new tutorials. Found a tutorial you loved? Tweet about it and tag us (@PyTorch). Ran into an issue you can help fix? File an issue in https://github.com/pytorch/tutorials. We are excited to continue building the future of machine learning with you!

To learn more about Facebook Open Source, subscribe on YouTube, or follow us on Twitter and Facebook.

Facebook Developers

Continue Reading

Trending