implementing lots of things from benbot

This commit is contained in:
Ben Harris 2017-12-05 00:59:37 -05:00
parent 425538ab0c
commit 8b0767d909
21 changed files with 668 additions and 59 deletions

View File

@ -1,5 +1,6 @@
using Discord.Commands;
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@ -18,17 +19,25 @@ namespace dotbot.Commands
[Summary("creates ascii word art")]
public async Task CreateAsciiArt(
[Summary("font you want to use")] string fontName,
[Remainder] [Summary("text to convert")] string ArtString
[Remainder] [Summary("text to convert")] string message = ""
) {
if (fontName == "list") {
await ReplyAsync($"available fonts for use with `{_config["prefix"]}ascii`:\n```{string.Join(", ", Directory.GetFiles("Fonts").ToList().Select(Path.GetFileNameWithoutExtension))}```");
} else if (File.Exists($"Fonts/{fontName}.flf")) {
if (fontName == "list")
{
var fontList = Directory.GetFiles("Fonts").ToList().Select(Path.GetFileNameWithoutExtension);
var joinedList = string.Join(", ", fontList);
Console.WriteLine(joinedList);
await ReplyAsync($"available fonts for use with `{_config["prefix"]}ascii`:\n```{joinedList}```");
}
else if (File.Exists($"Fonts/{fontName}.flf"))
{
using (FileStream fs = File.OpenRead($"Fonts/{fontName}.flf")) {
var font = new WenceyWang.FIGlet.FIGletFont(fs);
await ReplyAsync($"```\n{(new WenceyWang.FIGlet.AsciiArt(ArtString, font: font)).ToString()}\n```");
await ReplyAsync($"```\n{(new WenceyWang.FIGlet.AsciiArt(message, font: font)).ToString()}\n```");
}
} else {
await ReplyAsync($"```\n{(new WenceyWang.FIGlet.AsciiArt(fontName + ArtString)).ToString()}\n```");
}
else
{
await ReplyAsync($"```\n{(new WenceyWang.FIGlet.AsciiArt(fontName + message)).ToString()}\n```");
}
}
}

45
Commands/CleverBot.cs Normal file
View File

@ -0,0 +1,45 @@
using Discord.Commands;
using dotbot.Services;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
namespace dotbot.Commands
{
public class CleverBot : ModuleBase<SocketCommandContext>
{
private readonly IConfigurationRoot _config;
private Dictionary<ulong, string> _cache;
private string CleverBotAPIURL = "https://www.cleverbot.com/getreply";
public CleverBot(IConfigurationRoot config, CleverBotCacheService cache)
{
_config = config;
_cache = cache.Cache;
CleverBotAPIURL += $"?key={_config["tokens:cleverbot"]}&input=";
}
class CleverBotResponse
{
public string cs { get; set; }
public string output { get; set; }
}
[Command("b")]
[Alias("cleverbot", "cl")]
[Summary("talk to benbot")]
public async Task ChatWithCleverBot([Remainder] [Summary("what you want to say to benbot")] string message)
{
var url = $"{CleverBotAPIURL}{message}";
if (_cache.ContainsKey(Context.Channel.Id))
url += $"&cs={_cache[Context.Channel.Id]}";
var json = (new WebClient { Proxy = null }).DownloadString(url);
var response = JsonConvert.DeserializeObject<CleverBotResponse>(json);
_cache[Context.Channel.Id] = response.cs;
await ReplyAsync(response.output);
}
}
}

77
Commands/Definitions.cs Normal file
View File

@ -0,0 +1,77 @@
using Discord.Commands;
using dotbot.Core;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace dotbot.Commands
{
public class Definitions : ModuleBase<SocketCommandContext>
{
[Command("set")]
[Alias("define")]
[Summary("save some text for later")]
public async Task SetDefinition([Summary("key to set")] string Key, [Remainder] [Summary("what to set it to")] string Value)
{
using (var db = new DotbotDbContext())
{
if (db.Defs.Any(c => c.Id == Key))
db.Defs.Find(Key).Def = Value;
else
db.Defs.Add(new Definition { Id = Key, Def = Value });
db.SaveChanges();
await ReplyAsync($"`{Key}` set to `{Value}`");
}
}
[Command("get")]
[Summary("get some text that was saved")]
public async Task GetDefinition([Summary("key to look for")] string Key)
{
using (var db = new DotbotDbContext())
{
var def = db.Defs.Find(Key);
await ReplyAsync($"**{Key}**: {def?.Def ?? "not set"}");
}
}
[Command("unset")]
[Summary("remove something you saved before")]
public async Task RemoveDefinition([Summary("key to remove")] string Key)
{
using (var db = new DotbotDbContext())
{
if (db.Defs.Any(d => d.Id == Key))
{
db.Defs.Remove(db.Defs.Find(Key));
db.SaveChanges();
await ReplyAsync($"**{Key}** removed successfully");
}
else
{
await ReplyAsync($"**{Key}** doesn't exist");
}
}
}
[Command("defs")]
[Summary("print all saved definitions")]
public async Task GetAllDefinitions()
{
using (var db = new DotbotDbContext())
{
var res = new StringBuilder();
foreach (var def in db.Defs)
{
res.AppendLine($"`{def.Id}`: {def.Def}");
}
await ReplyAsync(res.ToString());
}
}
}
}

74
Commands/Emails.cs Normal file
View File

@ -0,0 +1,74 @@
using Discord;
using Discord.Commands;
using dotbot.Core;
using Microsoft.Extensions.Configuration;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Threading.Tasks;
namespace dotbot.Commands
{
[Group("email")]
public class Emails : ModuleBase<SocketCommandContext>
{
private readonly IConfigurationRoot _config;
public Emails(IConfigurationRoot config)
{
_config = config;
}
[Command]
public async Task SendEmail(
[Summary("user to send to")] IUser recipient,
[Summary("message to send")] [Remainder] string message
) {
using (var db = new DotbotDbContext())
{
if (!db.Emails.Any(e => e.Id == recipient.Id))
{
await ReplyAsync($"{recipient.Mention} does not have a saved email");
return;
}
var status = await ReplyAsync($"sending message");
var smtp = new SmtpClient("smtp.gmail.com")
{
EnableSsl = true,
Port = 587,
Credentials = new NetworkCredential(_config["gmail_login"], _config["tokens:gmail"])
};
smtp.Send(
$"{Context.User.Username}-{Context.Guild.Name}@benbot.tilde.team",
db.Emails.Find(recipient.Id).EmailAddress,
$"benbot message from {Context.User.Username}",
message
);
await Context.Message.DeleteAsync();
await status.ModifyAsync(m => m.Content = $"message sent to {recipient.Mention}!");
}
}
[Command("save")]
[Summary("saves an email address to your profile")]
public async Task SaveEmail([Summary("email to save")] string email)
{
using (var db = new DotbotDbContext())
{
await Context.Message.DeleteAsync();
db.Emails.Add(new Email
{
Id = Context.User.Id,
EmailAddress = email
});
db.SaveChanges();
await ReplyAsync("your email has been saved");
}
}
}
}

View File

@ -23,7 +23,13 @@ namespace dotbot.Commands
public async Task RollDie([Summary("[number of sides]")] int sides = 6)
{
await Context.Message.DeleteAsync();
await ReplyAsync($"{Context.User.Mention}, you rolled a {_rand.Next(1, sides)}. (d{sides})");
var embed = new EmbedBuilder()
{
Title = "Dice Roll :game_die:",
Color = new Color(255, 0, 0),
Description = $"{Context.User.Mention} rolled a {_rand.Next(1, sides)} (d{sides})"
};
await ReplyAsync("", false, embed);
}
@ -77,6 +83,7 @@ namespace dotbot.Commands
[Command("avatar")]
[Alias("pfp", "profilepic")]
[Summary("displays a user's profile picture")]
public async Task SendAvatar([Summary("user to get pfp for")] IUser mentionedUser = null)
{
@ -84,5 +91,52 @@ namespace dotbot.Commands
await ReplyAsync($"the avatar for {user.Mention} is at {user.GetAvatarUrl(size: 1024)}");
}
[Command("lenny")]
[Summary("you should know what this does")]
public async Task Lenny()
{
await ReplyAsync(@"( ͡° ͜ʖ ͡°)");
}
[Command("shrug")]
[Alias("meh")]
public async Task Shrug()
{
await ReplyAsync(@"¯\\\_(ツ)\_/¯");
}
[Command("noice")]
public async Task Noice()
{
await ReplyAsync(@"
:ok_hand: :joy:
:ok_hand::joy:
  :joy:
:joy::ok_hand:
:joy: :ok_hand:
:joy:  :ok_hand:
:joy:  :ok_hand:
:joy: :ok_hand:
:joy: :ok_hand:
  :ok_hand:
 :ok_hand: :joy:
:ok_hand:  :joy:
:ok_hand:  :joy:
:ok_hand: :joy:
:ok_hand::joy:
  :joy:
:joy::ok_hand:
:joy: :ok_hand:
:joy:  :ok_hand:
:joy:  :ok_hand:
:joy: :ok_hand:
:joy: :ok_hand:
  :ok_hand:");
}
}
}

8
Core/Definition.cs Normal file
View File

@ -0,0 +1,8 @@
namespace dotbot.Core
{
public class Definition
{
public string Id { get; set; }
public string Def { get; set; }
}
}

16
Core/DotbotDbContext.cs Normal file
View File

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
namespace dotbot.Core
{
public class DotbotDbContext : DbContext
{
public DbSet<Definition> Defs { get; set; }
public DbSet<Email> Emails { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=dotbot.db");
}
}
}

8
Core/Email.cs Normal file
View File

@ -0,0 +1,8 @@
namespace dotbot.Core
{
public class Email
{
public ulong Id { get; set; }
public string EmailAddress { get; set; }
}
}

View File

@ -0,0 +1,36 @@
// <auto-generated />
using dotbot.Core;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using System;
namespace dotbot.Migrations
{
[DbContext(typeof(DotbotDbContext))]
[Migration("20171205032404_Init")]
partial class Init
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.1-rtm-125");
modelBuilder.Entity("dotbot.Commands.Definition", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Def");
b.HasKey("Id");
b.ToTable("Defs");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace dotbot.Migrations
{
public partial class Init : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Defs",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Def = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Defs", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Defs");
}
}
}

View File

@ -0,0 +1,48 @@
// <auto-generated />
using dotbot.Core;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using System;
namespace dotbot.Migrations
{
[DbContext(typeof(DotbotDbContext))]
[Migration("20171205052158_AddEmailTable")]
partial class AddEmailTable
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.1-rtm-125");
modelBuilder.Entity("dotbot.Core.Definition", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Def");
b.HasKey("Id");
b.ToTable("Defs");
});
modelBuilder.Entity("dotbot.Core.Email", b =>
{
b.Property<ulong>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("EmailAddress");
b.HasKey("Id");
b.ToTable("Emails");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace dotbot.Migrations
{
public partial class AddEmailTable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Emails",
columns: table => new
{
Id = table.Column<ulong>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
EmailAddress = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Emails", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Emails");
}
}
}

View File

@ -0,0 +1,47 @@
// <auto-generated />
using dotbot.Core;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using System;
namespace dotbot.Migrations
{
[DbContext(typeof(DotbotDbContext))]
partial class DotbotDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.1-rtm-125");
modelBuilder.Entity("dotbot.Core.Definition", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Def");
b.HasKey("Id");
b.ToTable("Defs");
});
modelBuilder.Entity("dotbot.Core.Email", b =>
{
b.Property<ulong>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("EmailAddress");
b.HasKey("Id");
b.ToTable("Emails");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,6 +1,7 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using dotbot.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
@ -11,11 +12,7 @@ namespace dotbot
{
public class Program
{
private CommandService _commands;
private DiscordSocketClient _client;
private IConfigurationRoot _config;
private IServiceProvider _services;
private Random _rand;
public static void Main(string[] args)
=> new Program().StartAsync().GetAwaiter().GetResult();
@ -26,58 +23,33 @@ namespace dotbot
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("_config.json");
_config = builder.Build();
// get token from _config.json file
// if your bot isn't connecting, rename _config.example.json to _config.json
// and then place your bot token between the quotes
string token = _config["tokens:discord"];
Console.WriteLine(token);
_client = new DiscordSocketClient();
_commands = new CommandService();
_client.Log += Log;
var services = new ServiceCollection()
.AddSingleton(new DiscordSocketClient(new DiscordSocketConfig
{
LogLevel = LogSeverity.Verbose,
MessageCacheSize = 1000
}))
.AddSingleton(new CommandService(new CommandServiceConfig
{
LogLevel = LogSeverity.Verbose,
DefaultRunMode = RunMode.Async
}))
.AddSingleton<CommandHandlerService>()
.AddSingleton<LoggingService>()
.AddSingleton<StartupService>()
.AddSingleton<CleverBotCacheService>()
.AddSingleton<Random>()
.AddSingleton(_config);
_rand = new Random();
var provider = services.BuildServiceProvider();
_services = new ServiceCollection()
.AddSingleton(_client)
.AddSingleton(_commands)
.AddSingleton(_config)
.AddSingleton(_rand)
.BuildServiceProvider();
// install all commands from the assembly
_client.MessageReceived += HandleCommandAsync;
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();
provider.GetRequiredService<LoggingService>();
await provider.GetRequiredService<StartupService>().StartAsync();
provider.GetRequiredService<CommandHandlerService>();
await Task.Delay(-1);
}
private async Task HandleCommandAsync(SocketMessage arg)
{
var message = arg as SocketUserMessage;
if (message == null) return;
int argPos = 0;
if (!(message.HasStringPrefix(_config["prefix"], ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos))) return;
var context = new SocketCommandContext(_client, message);
var result = await _commands.ExecuteAsync(context, argPos, _services);
if (!result.IsSuccess)
await context.Channel.SendMessageAsync(result.ErrorReason);
}
private Task Log(LogMessage msg)
{
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
}
}
}

View File

@ -0,0 +1,15 @@
using Discord;
using System.Collections.Generic;
namespace dotbot.Services
{
public class CleverBotCacheService
{
public Dictionary<ulong, string> Cache { get; set; }
public CleverBotCacheService()
{
Cache = new Dictionary<ulong, string>();
}
}
}

View File

@ -0,0 +1,49 @@
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
using System;
using System.Threading.Tasks;
namespace dotbot.Services
{
public class CommandHandlerService
{
private readonly DiscordSocketClient _discord;
private readonly CommandService _commands;
private readonly IConfigurationRoot _config;
private readonly IServiceProvider _provider;
// DiscordSocketClient, CommandService, IConfigurationRoot, and IServiceProvider are injected automatically from the IServiceProvider
public CommandHandlerService(
DiscordSocketClient discord,
CommandService commands,
IConfigurationRoot config,
IServiceProvider provider)
{
_discord = discord;
_commands = commands;
_config = config;
_provider = provider;
_discord.MessageReceived += OnMessageReceivedAsync;
}
private async Task OnMessageReceivedAsync(SocketMessage s)
{
var msg = s as SocketUserMessage; // Ensure the message is from a user/bot
if (msg == null) return;
if (msg.Author.Id == _discord.CurrentUser.Id) return; // Ignore self when checking commands
var context = new SocketCommandContext(_discord, msg); // Create the command context
int argPos = 0; // Check if the message has a valid command prefix
if (msg.HasStringPrefix(_config["prefix"], ref argPos) || msg.HasMentionPrefix(_discord.CurrentUser, ref argPos))
{
var result = await _commands.ExecuteAsync(context, argPos, _provider); // Execute the command
if (!result.IsSuccess) // If not successful, reply with the error.
await context.Channel.SendMessageAsync(result.ToString());
}
}
}
}

View File

@ -0,0 +1,42 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using System;
using System.IO;
using System.Threading.Tasks;
namespace dotbot.Services
{
public class LoggingService
{
private readonly DiscordSocketClient _discord;
private readonly CommandService _commands;
private string _logDirectory { get; }
private string _logFile => Path.Combine(_logDirectory, $"{DateTime.UtcNow.ToString("yyyy-MM-dd")}.txt");
public LoggingService(DiscordSocketClient discord, CommandService commands)
{
_logDirectory = Path.Combine(AppContext.BaseDirectory, "logs");
_discord = discord;
_commands = commands;
_discord.Log += OnLogAsync;
_commands.Log += OnLogAsync;
}
private Task OnLogAsync(LogMessage msg)
{
if (!Directory.Exists(_logDirectory))
Directory.CreateDirectory(_logDirectory);
if (!File.Exists(_logFile))
File.Create(_logFile).Dispose();
string logText = $"{DateTime.UtcNow.ToString("hh:mm:ss")} [{msg.Severity}] {msg.Source}: {msg.Exception?.ToString() ?? msg.Message}";
File.AppendAllText(_logFile, logText + "\n");
return Console.Out.WriteLineAsync(logText);
}
}
}

View File

@ -0,0 +1,39 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
using System;
using System.Reflection;
using System.Threading.Tasks;
namespace dotbot.Services
{
public class StartupService
{
private readonly DiscordSocketClient _discord;
private readonly CommandService _commands;
private readonly IConfigurationRoot _config;
public StartupService(
DiscordSocketClient discord,
CommandService commands,
IConfigurationRoot config)
{
_config = config;
_discord = discord;
_commands = commands;
}
public async Task StartAsync()
{
string token = _config["tokens:discord"];
if (string.IsNullOrWhiteSpace(token))
throw new Exception("Please enter your bot's token into the `_config.json` file found in the applications root directory.");
await _discord.LoginAsync(TokenType.Bot, token);
await _discord.StartAsync();
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
}
}
}

View File

@ -1,6 +1,9 @@
{
"prefix": "!",
"gmail_login": "",
"tokens": {
"discord": ""
"discord": "",
"cleverbot": "",
"gmail": ""
}
}

View File

@ -6,9 +6,15 @@
<ItemGroup>
<PackageReference Include="Discord.Net" Version="1.0.2" />
<PackageReference Include="FIGlet.Net" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="_config.json">

BIN
dotbot.db Normal file

Binary file not shown.