Discord ბოტი C#-ზე არის .NET კონსოლის აპლიკაცია, რომელიც Discord-ის gateway-სთან ერთ გამავალ WebSocket კავშირს ინახავს და მოვლენებზე რეაგირებს. აირჩიე ბიბლიოთეკა - Discord.Net ყველაზე ფართოდ გამოყენებული და დოკუმენტირებულია, DSharpPlus დიდი ხნის ალტერნატივაა, NetCord ყველაზე ახალია და თანამედროვე .NET-ისთვისაა აგებული - ჩართე მხოლოდ ის gateway intent-ები, რომლებიც გჭირდება, დაარეგისტრირე slash ბრძანებები შეტყობინების ტექსტის პარსინგის ნაცვლად, და მთელი სისტემა .NET Generic Host-ის ქვეშ გაუშვი, რომ სწორად ეშვებოდეს, ლოგავდეს და ითიშებოდეს. მას შემავალი პორტი არ სჭირდება, CPU ძალიან ცოტა სჭირდება, ხოლო მეხსიერება ბოტების უმეტესობისთვის 60-დან 200 MB-მდე, რის გამოც ჰოსტინგის ყველაზე პატარა გეგმა ჩვეულებრივ საკმარისია.
ეს პოსტი Discord.Net-ით ბოტს აწყობს ცარიელი პროექტიდან slash ბრძანებამდე, შემდეგ კი განიხილავს, რა იცვლება, როცა ის სერვერზე დღე-ღამის განმავლობაში მუშაობს.
ბიბლიოთეკის არჩევა#
სამივე ბიბლიოთეკა ფარავს gateway-ს, REST API-ს, slash ბრძანებებს, ღილაკებს, select მენიუებს და modal-ებს. განსხვავება სიმწიფეში, სტილსა და იმაშია, რამდენად ახლოს მიჰყვება თითოეული Discord-ის უახლეს ფუნქციებს.
| ბიბლიოთეკა | პაკეტი | სტილი | შენიშვნები |
|---|---|---|---|
| Discord.Net | Discord.Net | მოვლენებზე დაფუძნებული, მომწიფებული | მომხმარებლების ყველაზე დიდი ბაზა და ყველაზე მეტი მაგალითი |
| DSharpPlus | DSharpPlus | მოვლენებზე დაფუძნებული, extension პაკეტები | ვერსია 5 დიდხანს prerelease-შია; შეამოწმე, რომელს იყენებენ სახელმძღვანელოები |
| NetCord | NetCord | თანამედროვე .NET, hosting-ზე ორიენტირებული | უფრო ახალია, მხოლოდ მიმდინარე .NET-ს უმიზნებს, ახლოსაა Discord API-სთან |
აირჩიე Discord.Net, თუ გინდა, რომ ნებისმიერ კითხვაზე პასუხი ერთი ძიების მანძილზე იყოს. აირჩიე NetCord, თუ .NET 8-ზე ან უფრო ახალზე თავიდან იწყებ და მოგწონს ბიბლიოთეკები, რომლებიც თავიდანვე dependency injection-ისა და Generic Host-ის გარშემოა დაპროექტებული. აირჩიე DSharpPlus, თუ უკვე იცნობ; სხვა შემთხვევაში გადაწყვეტილებამდე შეამოწმე მისი მე-5 ვერსიის მიმდინარე მდგომარეობა, რადგან მე-4 ვერსიისთვის დაწერილი მაგალითები პირდაპირ არ გადადის.
ქვემოთ მოცემული მაგალითები Discord.Net 3.x-ს იყენებს. კონცეფციები - intent-ები, ბრძანებების რეგისტრაცია, interaction-ების დადასტურება, ჰოსტი - Discord-ისაა და არა ბიბლიოთეკის, და სამივეზე ვრცელდება.
აპლიკაცია, ტოკენი და intent-ები#
ნებისმიერ კოდამდე ბოტი Discord Developer Portal-ში შექმენი:
- შექმენი New Application, შემდეგ გახსენი Bot გვერდი.
- გააკეთე ტოკენის reset და დააკოპირე. ის ერთხელ ჩანს. ნებისმიერს, ვისაც ის აქვს, შეუძლია შენი ბოტის სახელით იმოქმედოს ყველა სერვერზე, სადაც ის არის.
- ჩართე ნებისმიერი privileged intent, რომელიც ბოტს სჭირდება (ქვემოთ).
- OAuth2-ში დააგენერირე მოწვევის URL
botდაapplications.commandsscope-ებით და მხოლოდ იმ ნებართვებით, რომლებსაც ბოტი რეალურად იყენებს. გახსენი და ბოტი სატესტო სერვერზე დაამატე.
Intent-ები წყვეტს, რომელ მოვლენებს გიგზავნის Discord. უმეტესობა ჩვეულებრივია; სამი privileged-ია და პორტალშიც უნდა ჩაირთოს:
| Intent | Privileged | გჭირდება |
|---|---|---|
Guilds | არა | თითქმის ყველაფრისთვის - სერვერისა და არხის მონაცემები |
GuildMessages | არა | შეტყობინებების მოვლენები სერვერებზე |
MessageContent | კი | იმ შეტყობინებების ტექსტი, რომლებიც ბოტს არ მიმართავს |
GuildMembers | კი | წევრების შემოსვლისა და წასვლის მოვლენები, წევრების სრული სიები |
GuildPresences | კი | ონლაინ სტატუსი და აქტივობები |
ის, რაზეც ყველა იჭრება, არის MessageContent. მის გარეშე შეტყობინებების მოვლენები მაინც მოდის, მაგრამ Content ცარიელი სტრიქონია, გარდა იმ შეტყობინებებისა, რომლებიც ბოტს ახსენებს ან მისთვის განკუთვნილი პირადი შეტყობინებებია. slash ბრძანებებზე აგებულ ბოტს ის საერთოდ არ სჭირდება, რაც slash ბრძანებებზე აგების მთავარი მიზეზია. როცა ბოტი 100 ან მეტ სერვერზეა, privileged intent-ებს bot verification-ით Discord-ის თანხმობა სჭირდება, ამიტომ ნუ მოითხოვ იმას, რასაც არ იყენებ.
იგივე თავშეკავება ეხება მოწვევის URL-ში ნებართვებს. ცდუნებაა Administrator-ის მონიშვნა, რომ არაფერი ჩავარდეს "Missing Permissions"-ით, და ეს გაჟონილ ტოკენს უსიამოვნებიდან იმ ყველა სერვერის დაკარგვად აქცევს, სადაც ბოტია: თავდამსხმელს ადმინისტრატორის ბოტით შეუძლია არხების წაშლა, წევრების დაბლოკვა და როლების დარიგება. მიანიჭე კონკრეტული ნებართვები, რომლებსაც ბრძანებები იყენებს - Send Messages, Embed Links, Manage Roles, თუ ბოტი როლებს ანიჭებს - და გახსოვდეს, რომ როლების იერარქია მაინც მოქმედებს: ბოტს მხოლოდ საკუთარ უმაღლეს როლზე დაბალი როლების მართვა შეუძლია, რა ნებართვებიც არ უნდა ჰქონდეს. Subuser-ები და მინიმალური პრივილეგია იმავე არგუმენტს ჰოსტინგის მხრიდან აკეთებს.
მინიმალური ბოტი Generic Host-ზე#
შექმენი worker პროექტი და დაამატე პაკეტები:
$ dotnet new worker -n TavernBot$ cd TavernBot$ dotnet add package Discord.NetGeneric Host უფასოდ გაძლევს კონფიგურაციას, ლოგირებას, dependency injection-სა და სუფთა გამორთვას. თავად ბოტი hosted სერვისია, რომელიც კლიენტს ჰოსტის გაშვებისას უშვებს და ჰოსტის გაჩერებისას აჩერებს:
using Discord;using Discord.Interactions;using Discord.WebSocket;var builder = Host.CreateApplicationBuilder(args);builder.Services.AddSingleton(new DiscordSocketConfig{ GatewayIntents = GatewayIntents.Guilds});builder.Services.AddSingleton<DiscordSocketClient>();builder.Services.AddSingleton(sp => new InteractionService(sp.GetRequiredService<DiscordSocketClient>()));builder.Services.AddHostedService<BotService>();builder.Build().Run();public sealed class BotService( DiscordSocketClient client, InteractionService interactions, IServiceProvider services, IConfiguration config, ILogger<BotService> log) : IHostedService{ public async Task StartAsync(CancellationToken ct) { client.Log += m => { log.LogInformation("{Message}", m.ToString()); return Task.CompletedTask; }; await interactions.AddModulesAsync(typeof(BotService).Assembly, services); client.Ready += async () => await interactions.RegisterCommandsToGuildAsync(config.GetValue<ulong>("Discord:GuildId")); client.InteractionCreated += async i => await interactions.ExecuteCommandAsync(new SocketInteractionContext(client, i), services); await client.LoginAsync(TokenType.Bot, config["Discord:Token"]); await client.StartAsync(); } public async Task StopAsync(CancellationToken ct) { await client.StopAsync(); await client.LogoutAsync(); }}ბევრი სახელმძღვანელო ჰოსტს გამოტოვებს და Main-ს await Task.Delay(-1)-ით ამთავრებს, რომ პროცესი ცოცხალი დარჩეს. ეს მუშაობს, სანამ სხვა რამე არ დაგჭირდება: სუფთა გამორთვა არ არის, ამიტომ ბოტი ყოველ restart-ზე მოთხოვნის შუაში კვდება; dependency injection არ არის, ამიტომ ყოველი მოდული მონაცემთა ბაზის საკუთარ კავშირს ქმნის; და კონფიგურაციის სისტემა არ არის, ამიტომ ტოკენი კოდში მყარად ჩაწერილი ხვდება. ჰოსტი ათიოდე დამატებითი ხაზი ჯდება და სამივე პრობლემას აშორებს. ეს ასევე ნიშნავს, რომ იგივე ბოტი მოგვიანებით შეიძლება ASP.NET Core აპლიკაციაში უცვლელად ჩაჯდეს, რადგან ვებ აპლიკაციაც Generic Host-ია.
ტოკენი კონფიგურაციიდან მოდის, ამიტომ სერვერზე ის გარემოს ცვლადია Discord__Token და არასოდეს სტრიქონი კოდში. ASP.NET Core-ის კონფიგურაცია და საიდუმლოებები ორმაგ ქვედა ტირეს ხსნის; იგივე კონფიგურაციის სისტემა worker-შიც მუშაობს. თუ ტოკენი ოდესმე საჯარო repository-ში მოხვდა, მაშინვე გააკეთე მისი reset პორტალში. Discord GitHub-ის secret scanning-ში მონაწილეობს და გაჟონილ ტოკენებს ხშირად თავად აუქმებს, მაგრამ ეს გადარჩენაა და არა გეგმა.
Slash ბრძანებები#
interaction framework-ით ბრძანება მოდულის მეთოდია:
public sealed class GeneralModule : InteractionModuleBase<SocketInteractionContext>{ [SlashCommand("ping", "Check the bot is alive")] public Task Ping() => RespondAsync($"Pong - {Context.Client.Latency} ms"); [SlashCommand("roll", "Roll a die")] public Task Roll([MinValue(2), MaxValue(100)] int sides = 6) => RespondAsync($"You rolled {Random.Shared.Next(1, sides + 1)}"); [SlashCommand("report", "Build a server report")] public async Task Report() { await DeferAsync(ephemeral: true); var text = await BuildSlowReportAsync(); await FollowupAsync(text, ephemeral: true); } private static async Task<string> BuildSlowReportAsync() { await Task.Delay(5000); return "All quiet."; }}Discord-ის interaction მოდელის სამი წესი ყოველ ბრძანებას განსაზღვრავს:
- უპასუხე სამ წამში. თუ ბრძანებას მეტი დრო სჭირდება - მონაცემთა ბაზის query, HTTP გამოძახება, ნებისმიერი ნელი რამ - ჯერ გამოიძახე
DeferAsync(). მომხმარებელი ხედავს "thinking"-ს, შენ კი 15 წუთი გაქვს, რომ რეალური პასუხიFollowupAsync-ით გაგზავნო. ბრძანება, რომელიც სამ წამს ვერ ასწრებს, აჩვენებს "The application did not respond", მაშინაც კი, თუ შენი კოდი წამის შემდეგ წარმატებით სრულდება. - დაარეგისტრირე ბრძანებები და არა მხოლოდ გამოაცხადე. Discord-მა უნდა იცოდეს, რომ ბრძანება არსებობს, სანამ მომხმარებლები დაინახავენ. მაგალითი ერთ სატესტო სერვერზე არეგისტრირებს
RegisterCommandsToGuildAsync-ით, რაც მაშინვე მოქმედებს. production-ისთვის დაარეგისტრირე გლობალურადRegisterCommandsGloballyAsync-ით; გლობალური ცვლილებები ისტორიულად უფრო დიდხანს ჩნდებოდა, ამიტომ დეველოპმენტი guild-ზე აწარმოე და გამოშვებისას გადართე. - ნუ დაარეგისტრირებ თავიდან ყოველ გაშვებაზე საჭიროების გარეშე. რეგისტრაცია ბრძანებების მთელ ნაკრებს ანაცვლებს. ეს უვნებელია, მაგრამ API გამოძახებაა, და ბოტი, რომელიც ციკლში ვარდება და ყოველ ჯერზე თავიდან არეგისტრირებს, Discord-ის rate limit-ებს წააწყდება.
ოფციებს Discord ამოწმებს, სანამ შენამდე მოაღწევს - ზემოთ MinValue და MaxValue კლიენტში მოქმედებს - ამიტომ მეთოდი დიაპაზონში მყოფ მნიშვნელობებს იღებს. ღილაკები, select მენიუები და modal-ები იმავე მოდულებში [ComponentInteraction] და [ModalInteraction] handler-ებს იყენებს.
24/7 ჰოსტინგი#
gateway ბოტი Discord-ს გარედან უკავშირდება; მასთან შემოსული არაფერი უკავშირდება. მას არ სჭირდება პორტი, დომენი ან სერტიფიკატი. რაც სჭირდება, არის პროცესი, რომელიც ჩართული რჩება და ბრუნდება, როცა ჩავარდება, და ზუსტად ამას აკეთებს ჰოსტინგის პანელი. Discord ბოტის 24/7 ჰოსტინგი ზოგად ვარიანტებს განიხილავს, სათადარიგო კომპიუტერიდან VDS-მდე.
Git deploy-ის მქონე პანელზე start ბრძანება ბოტს აქვეყნებს და უშვებს:
dotnet publish -c Release -o out --disable-build-servers && exec dotnet out/TavernBot.dllexec ბოტს მთავარ პროცესად აქცევს, ამიტომ პანელის გაჩერების სიგნალი Generic Host-ს აღწევს, რომელიც StopAsync-ს იძახებს, ის კი gateway კავშირს სუფთად ხურავს. ამის გარეშე მოკლული ბოტი ცოტა ხნით ონლაინ ტოვებს "მოჩვენებას", სანამ Discord კავშირს timeout-ით არ დახურავს.
რას აქვს მნიშვნელობა სერვერზე:
- ლოგე კონსოლში. ჰოსტის კონსოლის logger stdout-ზე წერს, რასაც პანელი ცოცხლად აჩვენებს. gateway-ის გათიშვები და ხელახლა დაკავშირებები ნორმალურია; Discord.Net მათ ლოგავს და თავად უკავშირდება ხელახლა. ბოტს, რომელიც ყოველ რამდენიმე წამში ხელახლა დაკავშირებას ლოგავს, ქსელის ან ტოკენის პრობლემა აქვს.
- ელოდე restart-ებს. deploy-ები, განახლებები და დროდადრო crash-ები პროცესს რესტარტავს. ყველაფერი, რაც უნდა გადარჩეს - პარამეტრები სერვერის მიხედვით, მომხმარებლის მონაცემები, cooldown-ები - მეხსიერების გარეთ შეინახე.
- გქონდეს ცალკე სატესტო ბოტი. Developer Portal-ში დეველოპმენტისთვის მეორე აპლიკაცია შექმენი, საკუთარი ტოკენით, მხოლოდ კერძო სატესტო სერვერზე მოწვეული. შენი ლოკალური ასლის production ტოკენით გაშვება არის ის, თუ როგორ ებრძვის ორი პროცესი ერთ gateway სესიას და როგორ ჩნდება დაუმთავრებელი ბრძანებები შენი საზოგადოების სერვერებზე.
- დაუმუშავებელი გამონაკლისები event handler-ებში. Discord.Net handler-ებს საკუთარ task-ებზე უშვებს და მათგან გამონაკლისებს ლოგავს, მაგრამ handler, რომელიც დიდხანს ბლოკავს, gateway-ს აჩერებს. handler-ები მოკლე დატოვე და ნელი სამუშაო background task-ში ან რიგში გადაიტანე, როგორც აღწერილია პოსტში .NET worker სერვისები და background job-ები.
RE:NODE-ზე C# / .NET გეგმა ბოტს GitHub repository-დან ავრცელებს, ტოკენს Startup ჩანართზე გარემოს ცვლადად იღებს და პროცესს რესტარტავს, თუ ის გავა. Deploy on push მხოლოდ იმ სერვერს რესტარტავს, რომელიც უკვე მუშაობდა, ამიტომ ბოტი, რომელიც შეგნებულად გააჩერე, გაჩერებული რჩება. crash watcher მოულოდნელ restart-ებს ითვლის: საათში სამი სერვერის გვერდზე გაფრთხილებას აჩენს და ავტომატურად ხსნის ticket-ს, და ასე ხდება შესამჩნევი crash ციკლში გაჭედილი ბოტი. გეგმის არჩევა Discord ბოტისთვის ზომას განიხილავს.
მეხსიერება, ქეშირება და sharding#
ბოტის მეხსიერება ძირითადად Discord-ის მდგომარეობის ბიბლიოთეკისეული ქეშია: სერვერები, არხები, როლები და - თუ მოითხოვ - წევრები და შეტყობინებები.
პარამეტრი (DiscordSocketConfig) | ნაგულისხმევი | გავლენა მეხსიერებაზე |
|---|---|---|
MessageCacheSize | 0 | თითო არხზე შენახული შეტყობინებები; 0 არაფერს ინახავს |
AlwaysDownloadUsers | false | გაშვებისას ყოველი სერვერის ყოველი წევრის ჩამოტვირთვა |
GatewayIntents | არა-privileged ნაკრები | ნაკლები intent, ნაკლები მოვლენა და ნაკლები ქეში |
ბოტი რამდენიმე სერვერზე ნაგულისხმევი პარამეტრებით თავისუფლად რჩება 150 MB-ზე ქვემოთ. GuildMembers-ის ჩართვა AlwaysDownloadUsers-თან ერთად ათიათასობით წევრიან სერვერებზე არის ის, რაც ბოტს გიგაბაიტებამდე მიიყვანს. წევრები მხოლოდ იმ სერვერებისთვის და იმ მომენტებში ჩამოტვირთე, როცა გჭირდება.
worker პროექტი ნაგულისხმევად Workstation GC-ს იყენებს, რაც მეხსიერებას უფრო დაბლა ინახავს, ვიდრე ASP.NET Core აპლიკაცია; .NET-ის მეხსიერება და garbage collection განსხვავებას ხსნის. RE:NODE-ზე პროცესი, რომელიც გეგმის მეხსიერების ლიმიტს აღწევს, ჩერდება და სუფთად იწყება თავიდან და არა swap-ში რჩება, ამიტომ უსაზღვროდ მზარდი ქეში პერიოდული restart-ებით ჩანს და არა ნელი ბოტით.
Sharding gateway კავშირს რამდენიმე სესიად ყოფს, რომელთაგან თითოეული სერვერების ნაწილს ემსახურება. Discord ამას 2,500 სერვერიდან მოითხოვს. Discord.Net ამისთვის DiscordShardedClient-ს გთავაზობს. ამ ზღვარს ქვემოთ ერთი კავშირი უფრო მარტივი და სრულიად საკმარისია.
სად ინახავს ბოტი მონაცემებს#
ყველაფერი, რასაც მნიშვნელობა აქვს, პროცესს უნდა გადაურჩეს. ვარიანტები, ძალისხმევის მიხედვით:
- JSON ფაილი რამდენიმე პარამეტრისთვის. კარგია, სანამ ორი ბრძანება ერთდროულად არ ჩაწერს მასში; გამოიყენე lock.
- SQLite EF Core-ით ან
Microsoft.Data.Sqlite-ით. ერთი ფაილი, სერვერის გარეშე, რეალური ტრანზაქციები. ფაილი შეინახე იმ საქაღალდის გარეთ, რომელსაც შენი deploy ანაცვლებს. - მონაცემთა ბაზის სერვერი, როცა მონაცემებს ერთზე მეტი პროცესი კითხულობს, ან როცა გინდა, რომ backup-ებზე სხვამ იზრუნოს. აქ აპლიკაციის გეგმაში შედის ორი database slot დაგენერირებული მონაცემებით; .NET PostgreSQL-თან, MySQL-თან ან SQL Server-თან provider-ებს განიხილავს.
cooldown-ები და მომხმარებლის rate limit-ები შეიძლება მეხსიერებაში ინახებოდეს, თუ restart-ზე მათი დაკარგვა მისაღებია, რაც ბოტისთვის ჩვეულებრივ ასეა.
პრობლემების მოგვარება#
ბოტი ონლაინ არის, მაგრამ slash ბრძანებები არ ჩანს. ისინი არ დარეგისტრირდა, სხვა სერვერზე დარეგისტრირდა, ან მოწვევას applications.commands scope აკლდა. მოიწვიე თავიდან ორივე scope-ით და შეამოწმე, რომ Ready handler-ში რეგისტრაციის გამოძახება ლოგში შეცდომის გარეშე გაეშვა.
"The application did not respond". ბრძანებამ სამ წამზე მეტი დრო წაიღო defer-ის გარეშე, ან პასუხამდე გამონაკლისი ისროლა. შეამოწმე ლოგი, შემდეგ დაამატე DeferAsync() ყველაფერს, რაც I/O-ს აკეთებს.
შეტყობინების შიგთავსი ცარიელია. MessageContent intent აკლია კოდში, პორტალში ან ორივეგან. ან ფუნქცია slash ბრძანებაზე გადაიტანე.
"Authentication failed" ან 401 login-ისას. ტოკენი არასწორია, გარემოს ცვლადიდან შემთხვევითი ბრჭყალი ან ჰარი შეჰყვა, ან მისი reset გაკეთდა. თავიდან დააკოპირე.
ბოტი გამუდმებით ითიშება. ერთი და იმავე ტოკენით ორი პროცესი მუშაობს - ხშირად დავიწყებული ლოკალური ასლი და სერვერის ასლი - და Discord ერთ-ერთს გამუდმებით ხურავს. გააჩერე ლოკალური.
FAQ#
რომელი ბიბლიოთეკაა საუკეთესო პირველი C# ბოტისთვის?
Discord.Net, ძირითადად ხელმისაწვდომი მაგალითებისა და პასუხების სიმრავლის გამო. NetCord ძლიერი არჩევანია ახალი ბოტისთვის მიმდინარე .NET-ზე, თუ Generic Host-თან თავს კომფორტულად გრძნობ.
სჭირდება Discord ბოტს საჯარო IP ან ღია პორტი?
gateway ბოტს არა, ის გარეთ უკავშირდება. საჯარო HTTPS endpoint მხოლოდ იმ ბოტს სჭირდება, რომელიც interaction-ებს gateway-ის ნაცვლად HTTP-ით იღებს; აპლიკაციის გეგმაზე proxy slot მას ავტომატური სერტიფიკატით უზრუნველყოფს.
რამდენი მეხსიერება სჭირდება C# Discord ბოტს?
ჩვეულებრივ 60-დან 200 MB-მდე ბოტისთვის სერვერების ზომიერ რაოდენობაზე ნაგულისხმევი ქეშირებით. ამას ცვლის დიდი სერვერებისთვის წევრების სრული სიების ჩამოტვირთვა.
შემიძლია ბოტისა და ვებ dashboard-ის ერთ აპლიკაციაში გაშვება?
კი. გამოიყენე ASP.NET Core აპლიკაცია და ბოტი მასში hosted სერვისად დაარეგისტრირე; ორივე საერთო კონფიგურაციასა და მონაცემთა ბაზას იყენებს. dashboard-ს მაშინ პორტი და proxy slot სჭირდება, ბოტი კი თავის გამავალ gateway კავშირს ინარჩუნებს.
რატომ ჩანს ჩემი ბოტი ონლაინ გაჩერების შემდეგ კიდევ ცოტა ხანს?
ის მოკლეს და არა გააჩერეს, ამიტომ gateway სესია არასოდეს დაუხურავს, და Discord ელოდება, სანამ კავშირი timeout-ით დაიხურება. დარწმუნდი, რომ start ბრძანება exec-ს იყენებს და ჰოსტი StopAsync-ს იძახებს.




კომენტარები
სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.