Initial Commit

This commit is contained in:
Brazman 2022-09-10 14:56:21 -05:00
commit 5da3b3de02
13 changed files with 1210 additions and 0 deletions

63
.gitattributes vendored Normal file
View File

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

350
.gitignore vendored Normal file
View File

@ -0,0 +1,350 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/

24
LICENSE.txt Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

15
README.md Normal file
View File

@ -0,0 +1,15 @@
<br />
<div align="center">
<a href="https://github.com/Brazmann/SuperMachoBot/SuperMachoBot">
<img src="https://github.com/Brazmann/SuperMachoBot/blob/master/SuperMachoBot/Images/Logo.png" alt="Logo" width="256" height="256">
</a>
<h3 align="center">SuperMachoBot</h3>
<p align="center">
A Discord bot I made for fun with DSharpPlus! Includes D&D stat rolling, an economy system, and other cool stuff!
</p>
</div>
# Usage
Specify your bot token and discord User ID in config.json and run!

25
SuperMachoBot.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32112.339
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SuperMachoBot", "SuperMachoBot\SuperMachoBot.csproj", "{6C66DD74-8188-4568-A9B6-A16A4A47F9E4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6C66DD74-8188-4568-A9B6-A16A4A47F9E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6C66DD74-8188-4568-A9B6-A16A4A47F9E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C66DD74-8188-4568-A9B6-A16A4A47F9E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C66DD74-8188-4568-A9B6-A16A4A47F9E4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4D51C0D9-B4D1-4F4B-82E0-2B4EF0817B0C}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,191 @@
using DSharpPlus;
using DSharpPlus.CommandsNext;
using DSharpPlus.CommandsNext.Attributes;
using DSharpPlus.Entities;
using Newtonsoft.Json.Linq;
namespace SuperMachoBot.Commands
{
public class GeneralCommands : BaseCommandModule
{
public bool playMode = false;
public static Random random = new Random();
[Description("Sends a wholesome personalized greeting.")]
[Command("greet")]
public async Task GreetCommand(CommandContext ctx, [RemainingText] string name)
{
await ctx.RespondAsync($"Greetings, {name}! test");
}
[Description("Generate number between a specified minimum and maximum.")]
[Command("random")]
public async Task RandomCommand(CommandContext ctx, int min, int max)
{
var random = new Random();
await ctx.RespondAsync($"Your number is: {random.Next(min, max)}");
}
[Description("Ping specified user after a specified amount of time with a specified message.... specifically.")]
[Command("reminder")]
public async Task WaitCommand(CommandContext ctx, [Description("How long to wait")] float waittime, [Description("Unit of time")] string unit, [Description("User to ping")] DiscordUser user, [Description("Reminder message")][RemainingText] string remindmessage)
{
float delay = 10; //have to assign a dummy number at first because compiler is stupid
bool unitValid = true;
switch (unit)
{
case "seconds":
case "second":
delay = 1000 * waittime;
break;
case "minutes":
case "minute":
delay = 60000 * waittime;
break;
case "hours":
case "hour":
delay = 3600000 * waittime;
break;
default:
await ctx.RespondAsync("THATS NOT A VALID UNIT OF TIME!! https://tenor.com/view/soyjak-gif-23540164");
unitValid = false;
break;
}
if (unitValid == true)
{
int sleepDelay = (int)delay;
await ctx.RespondAsync($"Will ping in {waittime} {unit}");
Thread.Sleep(sleepDelay);
await ctx.RespondAsync($"{remindmessage}{user.Mention}");
}
}
[Description("Rolls dice with modifier one at a time")]
[Command("sroll")]
public async Task DebugCommand(CommandContext ctx, string dice, float modifier = 0)
{
var splitdice = dice.Split('d');
if (Int32.TryParse(splitdice[0], out int amount))
{
}
for (int i = 0; i < amount; i++)
{
Random rnd = new Random();
if (Int32.TryParse(splitdice[1], out int sides))
{
float diceroll = rnd.Next(1, sides + 1);
await ctx.RespondAsync($"rolled {diceroll} + {modifier} = {diceroll + modifier}");
}
else
{
await ctx.RespondAsync("Invalid dice!");
}
}
}
Random rnd = new Random();
[Description("Rolls dice the specified amount of times, then adds the modifier onto the total")]
[Command("mroll")]
public async Task MultipleRollCommand(CommandContext ctx, string dice, float modifier = 0)
{
//seperate the letter 'd' from dice
var splitdice = dice.Split('d');
//check if the first part is a number
if (Int32.TryParse(splitdice[0], out int amount))
{
//check if the second part is a number
if (Int32.TryParse(splitdice[1], out int sides))
{
//roll the dice
float total = 0;
for (int i = 0; i < amount; i++)
{
float diceroll = rnd.Next(1, sides + 1);
total += diceroll;
}
//add the modifier
total += modifier;
//send the result
await ctx.RespondAsync($"{total}");
}
else
{
await ctx.RespondAsync("Invalid dice!");
}
}
else
{
await ctx.RespondAsync("Invalid dice!");
}
}
[Description("Rolls character stats for d&d 5e using the 4d6k3 calculation")]
[Command("statroller")]
public async Task StatRollerCommand(CommandContext ctx)
{
int[] stats = { StatRoller(), StatRoller(), StatRoller(), StatRoller(), StatRoller(), StatRoller() };
await ctx.RespondAsync($"{stats[0]}\n{stats[1]}\n{stats[2]}\n{stats[3]}\n{stats[4]}\n{stats[5]}");
}
public int StatRoller()
{
Console.WriteLine("Stat is being rolled!");
int[] rolls = { rnd.Next(1, 6), rnd.Next(1, 6), rnd.Next(1, 6), rnd.Next(1, 6) };
int result = rolls[0] + rolls[1] + rolls[2] + rolls[3] - rolls.Min();
return result;
}
[Command("betflip")]
public async Task BetflipCommand(CommandContext ctx, string choice)
{
int flip = rnd.Next(1, 3); // 1 = heads, 2 = tails
if (choice.ToLower() == "heads")
{
switch (flip)
{
case 1:
await ctx.RespondAsync(embed: new DiscordEmbedBuilder { Title = "Heads! You win!", ImageUrl = "https://cdn.discordapp.com/attachments/897548763021864970/972648591107698698/domcoinheads.png" }.Build());
break;
case 2:
await ctx.RespondAsync(embed: new DiscordEmbedBuilder { Title = "Tails! You lose!", ImageUrl = "https://cdn.discordapp.com/attachments/897548763021864970/972648580185718834/domcointails.png" }.Build());
break;
}
}
if (choice.ToLower() == "tails")
{
switch (flip)
{
case 1:
await ctx.RespondAsync("Heads! You lose!");
await ctx.RespondAsync(embed: new DiscordEmbedBuilder { Title = "Heads! You lose!", ImageUrl = "https://cdn.discordapp.com/attachments/897548763021864970/972648591107698698/domcoinheads.png" }.Build());
break;
case 2:
await ctx.RespondAsync(embed: new DiscordEmbedBuilder { Title = "Tails! You win!", ImageUrl = "https://cdn.discordapp.com/attachments/897548763021864970/972648580185718834/domcointails.png" }.Build());
break;
}
}
}
[Hidden]
[Command("setactivity")]
private async Task SetActivityCommand(CommandContext ctx)
{
ulong channel = ctx.Channel.Id;
ulong messageid = ctx.Message.Id;
ulong ownerid = Program.configItems[0].OwnerID;
if (ctx.User.Id == ownerid) //Set this to use the user ID of owner in config file.
{
DiscordActivity activity = new DiscordActivity();
DiscordClient discord = ctx.Client;
string input = Console.ReadLine();
activity.Name = input;
await discord.UpdateStatusAsync(activity);
return;
}
else
{
Console.WriteLine($"Secret setactivity command run by {ctx.User} in https://discord.com/channels/{ctx.Channel.GuildId}/{channel}/{messageid}!");
}
}
}
}

View File

@ -0,0 +1,388 @@
using DSharpPlus;
using DSharpPlus.Entities;
using DSharpPlus.SlashCommands;
using SuperMachoBot;
namespace SuperMachoBot.Commands
{
public class SlashCommands : ApplicationCommandModule
{
public static string rootPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
#region General Commands
[SlashCommand("Avatar", "Gets high resolution avatar of specified user.")]
public async Task AvatarCommand(InteractionContext ctx, [Option("user", "Discord user to grab avatar from")] DiscordUser du)
{
var color = new DiscordColor(2, 200, 2);
var embed = new DiscordEmbedBuilder
{
Title = du.Username,
Color = color,
ImageUrl = Tools.General.AvatarParser(du)
};
await ctx.CreateResponseAsync(embed);
}
Random rnd = new Random();
[SlashCommand("StatRoller", "Rolls character stats for d&d 5e using the 4d6k3 calculation")]
public async Task StatRollerCommand(InteractionContext ctx)
{
int[] stats = { StatRoller(), StatRoller(), StatRoller(), StatRoller(), StatRoller(), StatRoller() };
await ctx.CreateResponseAsync($"{stats[0]}\n{stats[1]}\n{stats[2]}\n{stats[3]}\n{stats[4]}\n{stats[5]}");
}
public int StatRoller()
{
int[] rolls = { rnd.Next(1, 7), rnd.Next(1, 7), rnd.Next(1, 7), rnd.Next(1, 7) };
int result = rolls[0] + rolls[1] + rolls[2] + rolls[3] - rolls.Min();
return result;
}
[SlashCommand("EmbedTest", "Tests discord embed feature lol")]
public async Task DebugEmbedCommand(InteractionContext ctx)
{
var color = new DiscordColor(200, 2, 2);
var embed = new DiscordEmbedBuilder
{
Title = "bruh",
Description = "bruh",
Color = color
};
await ctx.CreateResponseAsync(embed);
}
[SlashCommand("Banner", "Gets the banner of the current server.")]
public async Task GuildBannerCommand(InteractionContext ctx)
{
var bannerUrl = ctx.Guild.BannerUrl;
if (bannerUrl == null)
{
await ctx.CreateResponseAsync("Error! Current server does not have a banner!");
}
else
{
var embed = new DiscordEmbedBuilder
{
ImageUrl = bannerUrl
};
await ctx.CreateResponseAsync(embed);
}
}
[SlashCommand("UserInfo", "Gets info from user")]
public async Task UserInfoCommand(InteractionContext ctx, [Option("user", "Discord user to grab info from")] DiscordUser du)
{
var hashcode = du.GetHashCode();
string[] Info =
{
$"**Account Creation Date:** {du.CreationTimestamp}",
$"**Account ID:** {du.Id}",
$"**User Language:** (Currently not functional)",
$"**Is Bot?** {du.IsBot}",
$"**Is Discord Admin?:** {du.IsSystem}"
};
var embed = new DiscordEmbedBuilder
{
Title = $"{du.Username}#{du.Discriminator}",
Description = $"{Info[0]} \n {Info[1]} \n {Info[2]} \n {Info[3]} \n {Info[4]}",
ImageUrl = Tools.General.AvatarParser(du)
};
await ctx.CreateResponseAsync(embed);
}
#endregion
#region Economy Commands
[SlashCommand("Balance", "Checks your balance")]
public async Task BalanceCommand(InteractionContext ctx, [Option("User", "User to check balance of")] DiscordUser du)
{
var entry = EconDatabaseChecker(du.Id, ctx.Guild.Id);
var entryParsed = entry[0].Split('|');
if (entry[0] == "noentry")
{
await ctx.CreateResponseAsync("No entry found! Generating one, please try again.");
}
await ctx.CreateResponseAsync($"{du.Username}: ${entryParsed[1]}");
}
[ContextMenu(ApplicationCommandType.UserContextMenu, "Check balance")]
public async Task BalanceMenuCommand(ContextMenuContext ctx)
{
var entry = EconDatabaseChecker(ctx.TargetUser.Id, ctx.Guild.Id);
var entryParsed = entry[0].Split('|');
if (entry[0] == "noentry")
{
await ctx.CreateResponseAsync("No entry found! Generating one, please try again.");
}
await ctx.CreateResponseAsync($"{ctx.TargetUser.Username}: ${entryParsed[1]}");
}
[SlashCommand("Daily", "Adds $100 to your balance")]
public async Task DailyCommand(InteractionContext ctx)
{
var path = $@"{rootPath}\EconomyDatabase\{ctx.Guild.Id}.csv";
var amount = 100;
var entry = EconDatabaseChecker(ctx.User.Id, ctx.Guild.Id);
var entryParsed = entry[0].Split('|');
var entryNumber = Int32.Parse(entry[1]);
Int32 unixTimestamp = (Int32)(DateTime.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
if (entryParsed[2] == "none")
{
string[] lines = File.ReadAllLines(path);
lines[entryNumber - 1] = $"{entryParsed[0]}|{entryParsed[1]}|{unixTimestamp}|";
WriteAllLinesBetter(path, lines);
AddSubtractUserMoney(ctx.User.Id, ctx.Guild.Id, amount);
await ctx.CreateResponseAsync("First daily! Come back in 24 hours!");
}
else
{
Int32 secondsSinceLastDaily = unixTimestamp - Convert.ToInt32(entryParsed[2]);
if (secondsSinceLastDaily > 86400) //Check if a day has passed
{
string[] lines = File.ReadAllLines(path);
lines[entryNumber - 1] = $"{entryParsed[0]}|{entryParsed[1]}|{unixTimestamp}|";
WriteAllLinesBetter(path, lines);
AddSubtractUserMoney(ctx.User.Id, ctx.Guild.Id, amount);
await ctx.CreateResponseAsync("Daily claimed! Come back in 24 hours!");
}
else if (secondsSinceLastDaily < 86400)
{
var secondsUntilClaim = 86400 - secondsSinceLastDaily;
await ctx.CreateResponseAsync($"Daily already claimed! Come back in {secondsUntilClaim / 3600} hours!");
}
}
}
[SlashCommand("Transfer", "Transfer your money to another user.")]
public async Task TransferCommand(InteractionContext ctx, [Option("Amount", "Amount to transfer")] long amount, [Option("User", "User to transfer money to")] DiscordUser du)
{
if(amount < 0)
{
await ctx.CreateResponseAsync("Negative amount detected! Sorry, robbery has not been implemented yet!");
}
else
{
AddSubtractUserMoney(ctx.User.Id, ctx.Guild.Id, -amount);
AddSubtractUserMoney(du.Id, ctx.Guild.Id, amount);
await ctx.CreateResponseAsync($"${amount} transferred from {ctx.User.Mention} to {du.Mention}");
}
}
[SlashCommand("Betflip", "Heads or Tails coinflip!")]
public async Task BetflipCommand(InteractionContext ctx, [Option("Choice", "Heads or Tails? H or T? Choose your path wisely.")] string choice, [Option("Amount", "Real: (typing 'All' currently doesn't work, do it manually.)")] long betAmount)
{
var uid = ctx.User.Id;
var gid = ctx.Guild.Id;
var entry = EconDatabaseChecker(ctx.User.Id, ctx.Guild.Id);
var entryParsed = entry[0].Split('|');
var playerMoney = Convert.ToInt64(entryParsed[1]);
var moneyEarned = 0;
if (betAmount > playerMoney)
{
await ctx.CreateResponseAsync("You do not have enough money!");
}
else
{
int flip = rnd.Next(1, 3); // 1 = heads, 2 = tails
string decision = "";
string headsURL = "https://cdn.discordapp.com/attachments/978411926222684220/1006493578186469376/domcoinheads.png";
string tailsURL = "https://cdn.discordapp.com/attachments/978411926222684220/1006493587342622730/domcointails.png";
switch (choice.ToLower())
{
case "h":
case "head":
case "heads":
decision = "heads";
break;
case "t":
case "tail":
case "tails":
decision = "tails";
break;
}
if (decision.ToLower() == "heads")
{
switch (flip)
{
case 1:
await ctx.CreateResponseAsync(embed: new DiscordEmbedBuilder { Title = $"Heads! You win ${betAmount}!", ImageUrl = headsURL }.Build());
AddSubtractUserMoney(uid, gid, betAmount);
break;
case 2:
await ctx.CreateResponseAsync(embed: new DiscordEmbedBuilder { Title = $"Tails! You lose ${betAmount}!", ImageUrl = tailsURL }.Build());
AddSubtractUserMoney(uid, gid, -betAmount);
break;
}
}
if (decision.ToLower() == "tails")
{
switch (flip)
{
case 1:
await ctx.CreateResponseAsync("Heads! You lose!");
await ctx.CreateResponseAsync(embed: new DiscordEmbedBuilder { Title = $"Heads! You lose ${betAmount}!", ImageUrl = headsURL }.Build());
AddSubtractUserMoney(uid, gid, -betAmount);
break;
case 2:
await ctx.CreateResponseAsync(embed: new DiscordEmbedBuilder { Title = $"Tails! You win ${betAmount}!", ImageUrl = tailsURL }.Build());
AddSubtractUserMoney(uid, gid, betAmount);
break;
}
}
}
}
[SlashCommand("Wheel", "Roll the wheel of Macho Fortune!")]
public async Task WheelCommand(InteractionContext ctx, [Option("Amount", "Real: (typing 'All' currently doesn't work, do it manually.)")] long betAmount)
{
if(betAmount < 0)
{
await ctx.CreateResponseAsync("Negative numbers are not allowed!");
}
else{
var entry = EconDatabaseChecker(ctx.User.Id, ctx.Guild.Id);
var entryParsed = entry[0].Split('|');
double playerMoney = Convert.ToDouble(entryParsed[1]);
double moneyEarned = 0;
if (betAmount > playerMoney)
{
await ctx.CreateResponseAsync("You do not have enough money!");
} else
{
var roll = rnd.Next(1, 8);
double multiplier = 1;
bool shit = false;
switch (roll)
{
case 1:
multiplier = 2.4;
shit = true;
break;
case 2:
multiplier = 1.8;
shit = true;
break;
case 3:
multiplier = 1.4;
shit = true;
break;
case 4:
multiplier = 0;
break;
case 5:
multiplier = 1.4;
break;
case 6:
multiplier = 1.8;
break;
case 7:
multiplier = 2.4;
break;
}
if(shit == true)
{
moneyEarned = betAmount * multiplier;
AddSubtractUserMoney(ctx.User.Id, ctx.Guild.Id, -Convert.ToInt64(moneyEarned));
await ctx.CreateResponseAsync($"Money multiplied by -{multiplier}x, lost ${moneyEarned}! Sad!");
} else
{
moneyEarned = betAmount * multiplier;
AddSubtractUserMoney(ctx.User.Id, ctx.Guild.Id, Convert.ToInt64(moneyEarned));
await ctx.CreateResponseAsync($"Money multiplied by {multiplier}x, ${moneyEarned}!");
}
}
}
}
#endregion
#region Economy Tools (I really need to stuff this into the library)
public void AddSubtractUserMoney(ulong userID, ulong guildID, long amount)
{
var entry = EconDatabaseChecker(userID, guildID);
var path = $@"{rootPath}\EconomyDatabase\{guildID}.csv";
var entryNumber = Int32.Parse(entry[1]);
var entryParsed = entry[0].Split('|');
var currentAmount = entryParsed[1];
long finalAmount = Convert.ToInt64(currentAmount) + amount;
string[] lines = File.ReadAllLines(path);
lines[entryNumber - 1] = $"{userID}|{finalAmount.ToString()}|{entryParsed[2]}|";
WriteAllLinesBetter(path, lines);
}
//Thank you microsoft for requiring a rewrite of your entire method just to not have it add an extra new line at the end of a file. :tf:
public static void WriteAllLinesBetter(string path, params string[] lines)
{
if (path == null)
throw new ArgumentNullException("path");
if (lines == null)
throw new ArgumentNullException("lines");
using (var stream = File.OpenWrite(path))
using (StreamWriter writer = new StreamWriter(stream))
{
if (lines.Length > 0)
{
for (int i = 0; i < lines.Length - 1; i++)
{
writer.WriteLine(lines[i]);
}
writer.Write(lines[lines.Length - 1]);
}
}
}
public void MultiplyUserMoney(ulong userID, ulong guildID, float multiplier)
{
var entry = EconDatabaseChecker(userID, guildID);
var path = $@"{rootPath}\EconomyDatabase\{guildID}.csv";
var entryNumber = Int32.Parse(entry[1]);
var entryParsed = entry[0].Split('|');
var currentAmount = entryParsed[1];
float finalAmount = float.Parse(currentAmount) * multiplier;
string[] lines = File.ReadAllLines(path);
lines[entryNumber - 1] = $"{userID}|{finalAmount.ToString()}";
WriteAllLinesBetter(path, lines);
}
/// <summary>
/// Finds the economy database entry for the specified UserID, or creates a new entry for the server/user if an entry for one is missing.
/// </summary>
/// <returns>
/// The contents, and line number of the entry if found, in a string array.
/// </returns>
public static string[] EconDatabaseChecker(ulong userID, ulong guildID)
{
int lineCount = 0;
string[] entry = { "noentry", "bingus" };
var path = $@"{rootPath}\EconomyDatabase\{guildID}.csv";
if (File.Exists(path) == false)
{
string entryToCreate = $"{userID}|100|none";
File.AppendAllText(path, entryToCreate);
}
foreach (var line in File.ReadAllLines(path))
{
var entryparsed = line.Split('|');
lineCount++;
if (entryparsed[0] == userID.ToString())
{
entry[0] = line; //Contents of entry line
entry[1] = $"{lineCount}"; //Number of line in .csv file
break;
}
}
if (entry[0] == "noentry") //If after the file has been searched, no entry has been found, create a new one and stuff it in the 'entry' variable
{
string entryToCreate = $"{userID}|100|none";
File.AppendAllText(path, Environment.NewLine + entryToCreate);
return entry;
}
return entry;
}
#endregion
}
}

View File

@ -0,0 +1,6 @@
[
{
"token": "",
"OwnerID": 0
}
]

View File

@ -0,0 +1 @@
userid|money|timesincelastdailyunixtimestamp
1 userid money timesincelastdailyunixtimestamp

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 KiB

66
SuperMachoBot/Program.cs Normal file
View File

@ -0,0 +1,66 @@
using DSharpPlus;
using DSharpPlus.CommandsNext;
using DSharpPlus.SlashCommands;
using SuperMachoBot.Commands;
using Newtonsoft.Json;
namespace SuperMachoBot
{
class Program
{
public static string rootPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
public static bool moneyCooldown = true;
public static List<Config> configItems = new List<Config>();
static void Main(string[] args)
{
MainAsync().GetAwaiter().GetResult();
}
internal static async Task MainAsync()
{
using (StreamReader r = new StreamReader(@$"{rootPath}\config\config.json"))
{
string json = r.ReadToEnd();
configItems = JsonConvert.DeserializeObject<List<Config>>(json);
}
var discord = new DiscordClient(new DiscordConfiguration()
{
Token = configItems[0].Token,
TokenType = TokenType.Bot
});
var slash = discord.UseSlashCommands();
discord.MessageCreated += async (s, e) =>
{
if (e.Message.Content.Contains("money") && e.Message.Content.Contains("tenor") == false && moneyCooldown == false)
{
await e.Message.RespondAsync("https://tenor.com/view/money-breaking-bad-sleep-on-money-lay-on-money-money-pile-gif-5382667");
cooldown();
}
};
var commands = discord.UseCommandsNext(new CommandsNextConfiguration()
{
StringPrefixes = new[] { "tf" }
});
commands.RegisterCommands<GeneralCommands>();
slash.RegisterCommands<SlashCommands>();
await discord.ConnectAsync();
await Task.Delay(-1);
}
static void cooldown()
{
moneyCooldown = true;
Thread.Sleep(10000);
moneyCooldown = false;
}
}
public class Config
{
public string Token;
public ulong OwnerID;
}
}

View File

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<StartupObject></StartupObject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DSharpPlus" Version="4.3.0-nightly-01146" />
<PackageReference Include="DSharpPlus.CommandsNext" Version="4.2.0-nightly-01091" />
<PackageReference Include="DSharpPlus.Interactivity" Version="4.2.0-nightly-01091" />
<PackageReference Include="DSharpPlus.Lavalink" Version="4.3.0-nightly-01146" />
<PackageReference Include="DSharpPlus.SlashCommands" Version="4.3.0-nightly-01146" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta2" />
<Protobuf Include="**/*.proto" />
</ItemGroup>
<ItemGroup>
<None Update="Config\config.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="EconomyDatabase\Template.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,51 @@
using DSharpPlus.Entities;
using DSharpPlus;
using System.Net;
namespace SuperMachoBot.Tools
{
class General
{
/// <returns>
/// The status code of the specified url.
/// </returns>
public static int GetPage(String url)
{
try {
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
return (int)myHttpWebResponse.StatusCode;
}
catch (WebException e)
{
return 0;
}
}
/// <summary>
/// Checks the HTTP status code of the specified user's avatar url to determine if it's a gif or image.
/// </summary>
/// <returns>
/// The appropriate avatar url for the user.
/// </returns>
public static string AvatarParser(DiscordUser du)
{
var avatarUrl = du.GetAvatarUrl(ImageFormat.Gif);
if (GetPage(avatarUrl) == 200)
{
return du.GetAvatarUrl(ImageFormat.Gif);
}
else
{
return du.GetAvatarUrl(ImageFormat.Png);
}
}
}
class Economy
{
}
}