Add sample bots to the solution (#972)

* Add sample bots to the solution

* Fix missing attributes, show use of preconditions
This commit is contained in:
Christopher F
2018-04-29 11:16:27 -04:00
committed by GitHub
parent 73dc884d47
commit 217ec34ef0
8 changed files with 324 additions and 2 deletions

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,69 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
namespace _01_basic_ping_bot
{
// This is a minimal, barebones example of using Discord.Net
//
// If writing a bot with commands, we recommend using the Discord.Net.Commands
// framework, rather than handling commands yourself, like we do in this sample.
//
// You can find samples of using the command framework:
// - Here, under the 02_commands_framework sample
// - https://github.com/foxbot/DiscordBotBase - a barebones bot template
// - https://github.com/foxbot/patek - a more feature-filled bot, utilizing more aspects of the library
class Program
{
private DiscordSocketClient _client;
// Discord.Net heavily utilizes TAP for async, so we create
// an asynchronous context from the beginning.
static void Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult();
public async Task MainAsync()
{
_client = new DiscordSocketClient();
_client.Log += LogAsync;
_client.Ready += ReadyAsync;
_client.MessageReceived += MessageReceivedAsync;
// Tokens should be considered secret data, and never hard-coded.
await _client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
await _client.StartAsync();
// Block the program until it is closed.
await Task.Delay(-1);
}
private Task LogAsync(LogMessage log)
{
Console.WriteLine(log.ToString());
return Task.CompletedTask;
}
// The Ready event indicates that the client has opened a
// connection and it is now safe to access the cache.
private Task ReadyAsync()
{
Console.WriteLine($"{_client.CurrentUser} is connected!");
return Task.CompletedTask;
}
// This is not the recommmended way to write a bot - consider
// reading over the Commands Framework sample.
private async Task MessageReceivedAsync(SocketMessage message)
{
// The bot should never respond to itself.
if (message.Author.Id == _client.CurrentUser.Id)
return;
if (message.Content == "!ping")
await message.Channel.SendMessageAsync("pong!");
}
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.0-preview1-final" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Discord.Net.Commands\Discord.Net.Commands.csproj" />
<ProjectReference Include="..\..\src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,63 @@
using System.IO;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using _02_commands_framework.Services;
namespace _02_commands_framework.Modules
{
// Modules must be public and inherit from an IModuleBase
public class PublicModule : ModuleBase<SocketCommandContext>
{
// Dependency Injection will fill this value in for us
public PictureService PictureService { get; set; }
[Command("ping")]
[Alias("pong", "hello")]
public Task PingAsync()
=> ReplyAsync("pong!");
[Command("cat")]
public async Task CatAsync()
{
// Get a stream containing an image of a cat
var stream = await PictureService.GetCatPictureAsync();
// Streams must be seeked to their beginning before being uploaded!
stream.Seek(0, SeekOrigin.Begin);
await Context.Channel.SendFileAsync(stream, "cat.png");
}
// Get info on a user, or the user who invoked the command if one is not specified
[Command("userinfo")]
public async Task UserInfoAsync(IUser user = null)
{
user = user ?? Context.User;
await ReplyAsync(user.ToString());
}
// Ban a user
[Command("ban")]
[RequireContext(ContextType.Guild)]
// make sure the user invoking the command can ban
[RequireUserPermission(GuildPermission.BanMembers)]
// make sure the bot itself can ban
[RequireBotPermission(GuildPermission.BanMembers)]
public async Task BanUserAsync(IGuildUser user, [Remainder] string reason = null)
{
await user.Guild.AddBanAsync(user, reason: reason);
await ReplyAsync("ok!");
}
// [Remainder] takes the rest of the command's arguments as one argument, rather than splitting every space
[Command("echo")]
public Task EchoAsync([Remainder] string text)
// Insert a ZWSP before the text to prevent triggering other bots!
=> ReplyAsync('\u200B' + text);
// 'params' will parse space-separated elements into a list
[Command("list")]
public Task ListAsync(params string[] objects)
=> ReplyAsync("You listed: " + string.Join("; ", objects));
}
}

View File

@@ -0,0 +1,60 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Discord;
using Discord.WebSocket;
using Discord.Commands;
using _02_commands_framework.Services;
namespace _02_commands_framework
{
// This is a minimal example of using Discord.Net's command
// framework - by no means does it show everything the framework
// is capable of.
//
// You can find samples of using the command framework:
// - Here, under the 02_commands_framework sample
// - https://github.com/foxbot/DiscordBotBase - a barebones bot template
// - https://github.com/foxbot/patek - a more feature-filled bot, utilizing more aspects of the library
class Program
{
static void Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult();
public async Task MainAsync()
{
var services = ConfigureServices();
var client = services.GetRequiredService<DiscordSocketClient>();
client.Log += LogAsync;
services.GetRequiredService<CommandService>().Log += LogAsync;
await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
await client.StartAsync();
await services.GetRequiredService<CommandHandlingService>().InitializeAsync();
await Task.Delay(-1);
}
private Task LogAsync(LogMessage log)
{
Console.WriteLine(log.ToString());
return Task.CompletedTask;
}
private IServiceProvider ConfigureServices()
{
return new ServiceCollection()
.AddSingleton<DiscordSocketClient>()
.AddSingleton<CommandService>()
.AddSingleton<CommandHandlingService>()
.AddSingleton<HttpClient>()
.AddSingleton<PictureService>()
.BuildServiceProvider();
}
}
}

View File

@@ -0,0 +1,49 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
namespace _02_commands_framework.Services
{
public class CommandHandlingService
{
private readonly CommandService _commands;
private readonly DiscordSocketClient _discord;
private readonly IServiceProvider _services;
public CommandHandlingService(IServiceProvider services)
{
_commands = services.GetRequiredService<CommandService>();
_discord = services.GetRequiredService<DiscordSocketClient>();
_services = services;
_discord.MessageReceived += MessageReceivedAsync;
}
public async Task InitializeAsync()
{
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
}
public async Task MessageReceivedAsync(SocketMessage rawMessage)
{
// Ignore system messages, or messages from other bots
if (!(rawMessage is SocketUserMessage message)) return;
if (message.Source != MessageSource.User) return;
// This value holds the offset where the prefix ends
var argPos = 0;
if (!message.HasMentionPrefix(_discord.CurrentUser, ref argPos)) return;
var context = new SocketCommandContext(_discord, message);
var result = await _commands.ExecuteAsync(context, argPos, _services);
if (result.Error.HasValue &&
result.Error.Value != CommandError.UnknownCommand) // it's bad practice to send 'unknown command' errors
await context.Channel.SendMessageAsync(result.ToString());
}
}
}

View File

@@ -0,0 +1,20 @@
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
namespace _02_commands_framework.Services
{
public class PictureService
{
private readonly HttpClient _http;
public PictureService(HttpClient http)
=> _http = http;
public async Task<Stream> GetCatPictureAsync()
{
var resp = await _http.GetAsync("https://cataas.com/cat");
return await resp.Content.ReadAsStreamAsync();
}
}
}