1
0

Compare commits

..

9 Commits

Author SHA1 Message Date
4d16a67a2b add basic create and list reminder api 2023-08-03 13:57:12 +07:00
e5693edc49 fix filename
All checks were successful
continuous-integration/drone/pr Build is passing
2023-07-28 21:03:33 +07:00
1afcda905c try fix docker build
Some checks failed
continuous-integration/drone/pr Build is failing
2023-07-28 21:02:06 +07:00
0c0e788966 try build
Some checks failed
continuous-integration/drone/pr Build is failing
2023-07-28 20:53:41 +07:00
c6c9688708 try fix docker file 2023-07-28 17:53:18 +07:00
966241a4aa add solution items 2023-07-28 17:42:05 +07:00
033415a2b4 add files for automatic ci 2023-07-28 17:35:19 +07:00
7b9342e6e1 rework ReminderModule 2023-07-25 19:06:46 +08:00
16723fee4d refactoring 2023-07-25 19:04:53 +08:00
17 changed files with 279 additions and 267 deletions

16
.drone.yml Normal file
View File

@@ -0,0 +1,16 @@
kind: pipeline
type: exec
name: backend
steps:
- name: build
commands:
- docker-compose build
- name: publish
commands:
- docker-compose up -d
trigger:
branch:
- master

View File

@@ -1,6 +1,17 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Budet-cho-bot", "Budet-cho-bot\Budet-cho-bot.csproj", "{9BF9C0C5-C025-425B-AB1C-6153C3827A76}" # Visual Studio Version 17
VisualStudioVersion = 17.3.32901.215
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Budet-cho-bot", "Budet-cho-bot\Budet-cho-bot.csproj", "{9BF9C0C5-C025-425B-AB1C-6153C3827A76}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EB4C3182-A0DD-4386-8E3B-4DEE4A72F3E4}"
ProjectSection(SolutionItems) = preProject
.drone.yml = .drone.yml
.gitignore = .gitignore
Config.json = Config.json
docker-compose.yml = docker-compose.yml
EndProjectSection
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -13,4 +24,10 @@ Global
{9BF9C0C5-C025-425B-AB1C-6153C3827A76}.Release|Any CPU.ActiveCfg = Release|Any CPU {9BF9C0C5-C025-425B-AB1C-6153C3827A76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9BF9C0C5-C025-425B-AB1C-6153C3827A76}.Release|Any CPU.Build.0 = Release|Any CPU {9BF9C0C5-C025-425B-AB1C-6153C3827A76}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E871DB37-3CF2-4EE2-AC63-3288B409205C}
EndGlobalSection
EndGlobal EndGlobal

View File

@@ -20,4 +20,9 @@
</ContentWithTargetPath> </ContentWithTargetPath>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Responses\" />
<Folder Include="Requests\" />
</ItemGroup>
</Project> </Project>

View File

@@ -36,7 +36,6 @@ namespace Discord_Bot
return; return;
var context = new SocketCommandContext(_client, message); var context = new SocketCommandContext(_client, message);
int argPos = 0; int argPos = 0;
JObject config = Functions.GetConfig(); JObject config = Functions.GetConfig();

19
Budet-cho-bot/Dockerfile Normal file
View File

@@ -0,0 +1,19 @@
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["/Budet-cho-bot", "Budet-cho-bot"]
COPY ["/Config.json", "Config.json"]
RUN dotnet restore "Budet-cho-bot/Budet-cho-bot.csproj"
COPY . .
WORKDIR "/src/Budet-cho-bot"
RUN dotnet build "Budet-cho-bot.csproj" -c Release -o /app/publish
FROM build AS publish
RUN dotnet publish "Budet-cho-bot.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base as final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Budet-cho-bot.dll", "--environment=Development"]

View File

@@ -61,6 +61,18 @@ namespace Discord_Bot
return (JObject)JsonConvert.DeserializeObject(configJson.ReadToEnd()); return (JObject)JsonConvert.DeserializeObject(configJson.ReadToEnd());
} }
public static JToken GetConfigItem(string item)
{
var config = Functions.GetConfig();
if (config.TryGetValue(item, out var result))
{
return result;
}
var res = new JValue(0);
return res.Type == JTokenType.Null;
}
public static void SaveConfig(JObject config) public static void SaveConfig(JObject config)
{ {
using (StreamWriter file = File.CreateText(Directory.GetCurrentDirectory() + @"/Config.json")) using (StreamWriter file = File.CreateText(Directory.GetCurrentDirectory() + @"/Config.json"))

View File

@@ -0,0 +1,58 @@
using Budet_cho_bot.Models;
using Newtonsoft.Json.Linq;
using System.Linq;
namespace Budet_cho_bot.Helpers;
public static class Utility
{
public static DateTime ParseDate(string message)
{
if (TimeSpan.TryParse(message, out var parsedTime) == false)
{
throw new ArgumentException("Хуйня твоё время");
}
var today = DateTime.Today; //returns xx/xx/xxxx 00:00:00, so adding time span would set correct daate
var date = today + parsedTime;
return date;
}
public static TimeSpan DeserializeTime(string token)
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<TimeSpan>(token);
}
public static string PackTimetableIntoString(CronRecord[] timetable)
{
return string.Join(';', timetable.Select(x =>
{
var unixRunTime = x.LastRun.ToUniversalTime().ToUnixTimeSeconds();
var secondsPeriod = (int)(x.Period.TotalSeconds);
return $"{unixRunTime}:{secondsPeriod}:{x.IsOneShot}";
}));
}
public static CronRecord[] UnpackTimetableFromString(string timetable)
{
if(timetable == null)
{
return new CronRecord[0];
}
return timetable.Split(';').Select(x =>
{
var record = x.Split(':'); //crude but whatever
var lastRun = DateTimeOffset.FromUnixTimeSeconds(long.Parse(record[0]));
var period = TimeSpan.FromSeconds(long.Parse(record[1]));
bool oneShot = bool.Parse(record[2]);
return new CronRecord()
{
LastRun = lastRun,
Period = period,
IsOneShot = oneShot
};
}).ToArray();
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Budet_cho_bot.Models
{
public class CronRecord
{
public DateTimeOffset LastRun { set; get; }
public TimeSpan Period { set; get; }
public bool IsOneShot { set; get; }
}
}

View File

@@ -1,48 +0,0 @@
using Discord.Commands;
using Discord.WebSocket;
using System;
using System.Threading.Tasks;
namespace Discord_Bot
{
public class FunSample : ModuleBase<SocketCommandContext>
{
[Command("hello")] // Command name.
[Summary("Say hello to the bot.")] // Command summary.
public async Task Hello()
=> await ReplyAsync($"Hello there, **{Context.User.Username}**!");
[Command("pick")]
[Alias("choose")] // Aliases that will also trigger the command.
[Summary("Pick something.")]
public async Task Pick([Remainder]string message = "")
{
string[] options = message.Split(new string[] { " or " }, StringSplitOptions.RemoveEmptyEntries);
string selection = options[new Random().Next(options.Length)];
// ReplyAsync() is a shortcut for Context.Channel.SendMessageAsync()
await ReplyAsync($"I choose **{selection}**");
}
[Command("cookie")]
[Summary("Give someone a cookie.")]
public async Task Cookie(SocketGuildUser user)
{
if (Context.Message.Author.Id == user.Id)
await ReplyAsync($"{Context.User.Mention} doesn't have anyone to share a cookie with... :(");
else
await ReplyAsync($"{Context.User.Mention} shared a cookie with **{user.Username}** :cookie:");
}
[Command("amiadmin")]
[Summary("Check your administrator status")]
public async Task AmIAdmin()
{
if ((Context.User as SocketGuildUser).GuildPermissions.Administrator)
await ReplyAsync($"Yes, **{Context.User.Username}**, you're an admin!");
else
await ReplyAsync($"No, **{Context.User.Username}**, you're **not** an admin!");
}
}
}

View File

@@ -1,14 +0,0 @@
using Discord.Commands;
namespace Budet_cho_bot.Modules;
public class InfoModule : ModuleBase<SocketCommandContext>
{
// ~say hello world -> hello world
[Command("say")]
[Summary("Echoes a message.")]
public Task SayAsync([Remainder] [Summary("The text to echo")] string echo)
=> ReplyAsync(echo);
// ReplyAsync is a method on ModuleBase
}

View File

@@ -1,60 +0,0 @@
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
namespace Discord_Bot
{
public class ModSample : ModuleBase<SocketCommandContext>
{
[Command("kick")]
[Summary("Kick a user from the server.")]
[RequireBotPermission(GuildPermission.KickMembers)]
[RequireUserPermission(GuildPermission.KickMembers)]
public async Task Kick(SocketGuildUser targetUser, [Remainder]string reason = "No reason provided.")
{
await targetUser.KickAsync(reason);
await ReplyAsync($"**{targetUser}** has been kicked. Bye bye :wave:");
}
[Command("ban")]
[Summary("Ban a user from the server")]
[RequireUserPermission(GuildPermission.BanMembers)]
[RequireBotPermission(GuildPermission.BanMembers)]
public async Task Ban(SocketGuildUser targetUser, [Remainder]string reason = "No reason provided.")
{
await Context.Guild.AddBanAsync(targetUser.Id, 0, reason);
await ReplyAsync($"**{targetUser}** has been banned. Bye bye :wave:");
}
[Command("unban")]
[Summary("Unban a user from the server")]
[RequireBotPermission(GuildPermission.BanMembers)]
[RequireUserPermission(GuildPermission.BanMembers)]
public async Task Unban(ulong targetUser)
{
await Context.Guild.RemoveBanAsync(targetUser);
await Context.Channel.SendMessageAsync($"The user has been unbanned :clap:");
}
[Command("purge")]
[Summary("Bulk deletes messages in chat")]
[RequireBotPermission(GuildPermission.ManageMessages)]
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task Purge(int delNumber)
{
var channel = Context.Channel as SocketTextChannel;
var items = await channel.GetMessagesAsync(delNumber + 1).FlattenAsync();
await channel.DeleteMessagesAsync(items);
}
[Command("reloadconfig")]
[Summary("Reloads the config and applies changes")]
[RequireOwner] // Require the bot owner to execute the command successfully.
public async Task ReloadConfig()
{
await Functions.SetBotStatusAsync(Context.Client);
await ReplyAsync("Reloaded!");
}
}
}

View File

@@ -1,82 +1,69 @@
using Discord.Commands; using Budet_cho_bot.Helpers;
using Budet_cho_bot.Models;
using Budet_cho_bot.Services;
using Discord.Commands;
using Discord.Commands.Builders;
using Newtonsoft.Json.Linq;
using System.Text;
namespace Discord_Bot namespace Discord_Bot
{ {
public class ReminderModule : ModuleBase<SocketCommandContext> public class ReminderModule : ModuleBase<SocketCommandContext>
{ {
private long dayPeriod = 24 * 60 * 60 * 1000; private ReminderService _reminderService = new ReminderService();
private Timer reminderTimer;
[Command("remindertime")] protected override void OnModuleBuilding(CommandService commandService, ModuleBuilder builder)
public async Task ReminderTime([Remainder]string message = "")
{ {
SaveToConfig("reminderTime", message); base.OnModuleBuilding(commandService, builder);
Console.WriteLine($"{DateTime.Now.TimeOfDay:hh\\:mm\\:ss} | hello world ");
var time = message.Split(":");
SetReminderTime(int.Parse(time[0]), int.Parse(time[1]));
await ReplyAsync($"Заебись");
} }
[Command("calltime")] [Command("remind")]
public async Task CallTime([Remainder]string message = "") public async Task CreateTask(string users, DateTime startsAt, string? repeat)
{ {
SaveToConfig("callTime", message); bool oneShot = repeat == null;
var now = DateTime.Now;
TimeSpan rep;
TimeSpan.TryParse(repeat, out rep);
var time = message.Split(":"); var record = new CronRecord()
SetReminderTime(int.Parse(time[0]), int.Parse(time[1])); {
Period = oneShot ? startsAt - now : rep,
LastRun = oneShot ? now : startsAt,
IsOneShot = oneShot
};
var created = await _reminderService.CreateTask(record);
await ReplyAsync($"Заебись: {created}");
}
[Command("sequence")]
private async Task SetSequence(string message = "")
{
//_reminderService.SaveToConfig("sequence", message);
await ReplyAsync($"Заебись"); await ReplyAsync($"Заебись");
} }
[Command("users")] [Command("users")]
public async Task Users([Remainder]string message = "") private async Task Users([Remainder]string message = "")
{ {
SaveToConfig("users", message); //_reminderService.SaveToConfig("users", message);
var time = message.Split(":");
SetReminderTime(int.Parse(time[0]), int.Parse(time[1]));
await ReplyAsync($"Заебись"); await ReplyAsync($"Заебись");
} }
public void SetReminderTime(int hours, int minutes) [Command("list")]
{ private async Task List()
var now = DateTime.Now;
var nextReminder = new DateTime(now.Year, now.Month, now.Day, hours, minutes, 0);
var span = (nextReminder - now).TotalMilliseconds;
reminderTimer = new Timer(SendReminder, "sd", (long)span, dayPeriod);
ReloadTimers();
}
public void SetCallTime(int hours, int minutes)
{
var now = DateTime.Now;
var nextReminder = new DateTime(now.Year, now.Month, now.Day, hours, minutes, 0);
var span = (nextReminder - now).TotalMilliseconds;
reminderTimer = new Timer(SendReminder, "sd", (long)span, dayPeriod);
ReloadTimers();
}
private async void SendReminder(object? state)
{
var users = Functions.GetConfig()["users"].ToString();
await ReplyAsync($"{users}\nБудет чо?");
}
private async void SendCall(object? state)
{
await ReplyAsync($"?");
}
private void SaveToConfig(string key, string data)
{
var config = Functions.GetConfig();
config[key] = data;
Functions.SaveConfig(config);
}
private void ReloadTimers()
{ {
var cron = await _reminderService.GetTasks();
var sb = new StringBuilder();
sb.AppendLine("Last Run | Period | Is one shot");
sb.AppendLine("-------------------------------");
foreach (var cr in cron)
{
sb.AppendLine($"{cr.LastRun.ToString("o")} | {cr.Period.ToString("c")} | {cr.IsOneShot}");
}
sb.AppendLine("-------------------------------");
await ReplyAsync(sb.ToString());
} }
} }
} }

View File

@@ -1,79 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using System.Globalization;
namespace Discord_Bot
{
public class UtilitySample : ModuleBase<SocketCommandContext>
{
[Command("ping")]
[Summary("Show current latency.")]
public async Task Ping()
=> await ReplyAsync($"Latency: {Context.Client.Latency} ms");
[Command("avatar")]
[Alias("getavatar")]
[Summary("Get a user's avatar.")]
public async Task GetAvatar([Remainder]SocketGuildUser user = null)
=> await ReplyAsync($":frame_photo: **{(user ?? Context.User as SocketGuildUser).Username}**'s avatar\n{Functions.GetAvatarUrl(user)}");
[Command("info")]
[Alias("server", "serverinfo")]
[Summary("Show server information.")]
[RequireBotPermission(GuildPermission.EmbedLinks)] // Require the bot the have the 'Embed Links' permissions to execute this command.
public async Task ServerEmbed()
{
double botPercentage = Math.Round(Context.Guild.Users.Count(x => x.IsBot) / Context.Guild.MemberCount * 100d, 2);
EmbedBuilder embed = new EmbedBuilder()
.WithColor(0, 225, 225)
.WithDescription(
$"🏷️\n**Guild name:** {Context.Guild.Name}\n" +
$"**Guild ID:** {Context.Guild.Id}\n" +
$"**Created at:** {Context.Guild.CreatedAt:dd/M/yyyy}\n" +
$"**Owner:** {Context.Guild.Owner}\n\n" +
$"💬\n" +
$"**Users:** {Context.Guild.MemberCount - Context.Guild.Users.Count(x => x.IsBot)}\n" +
$"**Bots:** {Context.Guild.Users.Count(x => x.IsBot)} [ {botPercentage}% ]\n" +
$"**Channels:** {Context.Guild.Channels.Count}\n" +
$"**Roles:** {Context.Guild.Roles.Count}\n" +
$"**Emotes: ** {Context.Guild.Emotes.Count}\n\n" +
$"🌎 **Region:** {Context.Guild.VoiceRegionId}\n\n" +
$"🔒 **Security level:** {Context.Guild.VerificationLevel}")
.WithImageUrl(Context.Guild.IconUrl);
await ReplyAsync($":information_source: Server info for **{Context.Guild.Name}**", embed: embed.Build());
}
[Command("role")]
[Alias("roleinfo")]
[Summary("Show information about a role.")]
public async Task RoleInfo([Remainder]SocketRole role)
{
// Just in case someone tries to be funny.
if (role.Id == Context.Guild.EveryoneRole.Id)
return;
await ReplyAsync(
$":flower_playing_cards: **{role.Name}** information```ini" +
$"\n[Members] {role.Members.Count()}" +
$"\n[Role ID] {role.Id}" +
$"\n[Hoisted status] {role.IsHoisted}" +
$"\n[Created at] {role.CreatedAt:dd/M/yyyy}" +
$"\n[Hierarchy position] {role.Position}" +
$"\n[Color Hex] {role.Color}```");
}
// Please don't remove this command. I will appreciate it a lot <3
[Command("source")]
[Alias("sourcecode", "src")]
[Summary("Link the source code used for this bot.")]
public async Task Source()
=> await ReplyAsync($":heart: **{Context.Client.CurrentUser}** is based on this source code:\nhttps://github.com/VACEfron/Discord-Bot-Csharp");
}
}

View File

@@ -0,0 +1,33 @@
using Budet_cho_bot.Helpers;
using Discord_Bot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Budet_cho_bot.Repositories
{
public class CronRepository
{
public async Task<int> SetCronTask(string taskType, string task)
{
var config = Functions.GetConfig();
config[taskType] = task;
Functions.SaveConfig(config);
return 1;
}
public async Task<string> GetCronTask(string taskType)
{
var config = Functions.GetConfig();
if (config.TryGetValue(taskType, out var tasks))
{
return tasks.ToString();
}
return null;
}
}
}

View File

@@ -0,0 +1,44 @@
using Budet_cho_bot.Helpers;
using Budet_cho_bot.Models;
using Budet_cho_bot.Repositories;
using Discord;
using Discord_Bot;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Budet_cho_bot.Services
{
internal class ReminderService
{
private readonly CronRepository _cronRepository = new CronRepository();
private const long DAY_PERIOD = 24 * 60 * 60 * 1000; // 10 * 1000; //
private Timer reminderTimer;
public async void SendReminder(object? state)
{
var users = Functions.GetConfigItem("users").ToString();
//await ReplyAsync($"{users}\nБудет чо?");
}
public async Task<int> CreateTask(CronRecord record)
{
var cron = await _cronRepository.GetCronTask("reminder");
var cronUnpacked = Utility.UnpackTimetableFromString(cron);
var result = await _cronRepository.SetCronTask("reminder", Utility.PackTimetableIntoString(cronUnpacked.Append(record).ToArray()));
return result;
}
public async Task<CronRecord[]> GetTasks()
{
var cron = await _cronRepository.GetCronTask("reminder");
var cronUnpacked = Utility.UnpackTimetableFromString(cron);
return cronUnpacked;
}
}
}

View File

@@ -2,9 +2,8 @@
"version": "1.0", "version": "1.0",
"token": "MTA4NDA5MTA4OTE1NTI4MDkxNw.G77VAz.3O8uS-D4nwp1Ax8iZMBIx0Z9gsYNNOwPFfGRl8", "token": "MTA4NDA5MTA4OTE1NTI4MDkxNw.G77VAz.3O8uS-D4nwp1Ax8iZMBIx0Z9gsYNNOwPFfGRl8",
"prefixes": ["."], "prefixes": ["."],
"join_message": "Hello, I'm Discord Bot! :heart:", "join_message": "Дратуте! :heart:",
"currently": "playing|listening|watching|streaming", "currently": "playing",
"playing_status": "майныч", "playing_status": "майныч",
"status": "online|dnd|idle|offline", "status": "online"
"reminderTime": "1:12"
} }

9
docker-compose.yml Normal file
View File

@@ -0,0 +1,9 @@
version: '3.4'
services:
bot:
image: bot
container_name: bot
build:
context: ./
dockerfile: Budet-cho-bot/Dockerfile