Finish migration to MongoDb and general improvements
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,4 +3,5 @@ bin/
|
||||
obj/
|
||||
*.log
|
||||
*.json
|
||||
.env
|
||||
.env
|
||||
data/
|
@@ -1,53 +1,53 @@
|
||||
namespace CatsOfMastodonBot.Models
|
||||
namespace CatsOfMastodonBot.Models;
|
||||
|
||||
public class ConfigData
|
||||
{
|
||||
public class ConfigData
|
||||
public static config fetchData()
|
||||
{
|
||||
// Load from .env file first
|
||||
DotNetEnv.Env.Load();
|
||||
|
||||
public static config fetchData()
|
||||
// Fetch values from .env file or environment variables (fall back)
|
||||
var dbName = DotNetEnv.Env.GetString("DB_NAME") ??
|
||||
Environment.GetEnvironmentVariable("DB_NAME") ?? "catsofmastodon";
|
||||
var mongoDbConnectionString = DotNetEnv.Env.GetString("MONGODB_CONNECTION_STRING") ??
|
||||
Environment.GetEnvironmentVariable("MONGODB_CONNECTION_STRING");
|
||||
var botToken = DotNetEnv.Env.GetString("BOT_TOKEN") ?? Environment.GetEnvironmentVariable("BOT_TOKEN");
|
||||
var tag = DotNetEnv.Env.GetString("TAG") ?? Environment.GetEnvironmentVariable("TAG");
|
||||
var channelNumId = DotNetEnv.Env.GetString("CHANNEL_NUMID") ??
|
||||
Environment.GetEnvironmentVariable("CHANNEL_NUMID");
|
||||
var adminNumId = DotNetEnv.Env.GetString("ADMIN_NUMID") ?? Environment.GetEnvironmentVariable("ADMIN_NUMID");
|
||||
var instance = DotNetEnv.Env.GetString("CUSTOM_INSTANCE") ??
|
||||
Environment.GetEnvironmentVariable("CUSTOM_INSTANCE") ?? "https://haminoa.net";
|
||||
|
||||
// Check if any of the values are still null or empty
|
||||
if (string.IsNullOrEmpty(botToken) || string.IsNullOrEmpty(tag)
|
||||
|| string.IsNullOrEmpty(channelNumId) || string.IsNullOrEmpty(adminNumId))
|
||||
return null; // Failure if any are missing
|
||||
|
||||
// If all required variables are present, assign to the config
|
||||
var config = new config
|
||||
{
|
||||
// Load from .env file first
|
||||
DotNetEnv.Env.Load();
|
||||
DB_NAME = dbName,
|
||||
MONGODB_CONNECTION_STRING = mongoDbConnectionString,
|
||||
BOT_TOKEN = botToken,
|
||||
TAG = tag,
|
||||
CHANNEL_NUMID = channelNumId,
|
||||
ADMIN_NUMID = adminNumId,
|
||||
INSTANCE = instance
|
||||
};
|
||||
|
||||
// Fetch values from .env file or environment variables (fall back)
|
||||
string dbName = DotNetEnv.Env.GetString("DB_NAME") ?? Environment.GetEnvironmentVariable("DB_NAME") ?? "catsofmastodon";
|
||||
string mongoDbConnectionString = DotNetEnv.Env.GetString("MONGODB_CONNECTION_STRING") ?? Environment.GetEnvironmentVariable("MONGODB_CONNECTION_STRING");
|
||||
string botToken = DotNetEnv.Env.GetString("BOT_TOKEN") ?? Environment.GetEnvironmentVariable("BOT_TOKEN");
|
||||
string tag = DotNetEnv.Env.GetString("TAG") ?? Environment.GetEnvironmentVariable("TAG");
|
||||
string channelNumId = DotNetEnv.Env.GetString("CHANNEL_NUMID") ?? Environment.GetEnvironmentVariable("CHANNEL_NUMID");
|
||||
string adminNumId = DotNetEnv.Env.GetString("ADMIN_NUMID") ?? Environment.GetEnvironmentVariable("ADMIN_NUMID");
|
||||
string instance = DotNetEnv.Env.GetString("CUSTOM_INSTANCE") ?? Environment.GetEnvironmentVariable("CUSTOM_INSTANCE") ?? "https://haminoa.net";
|
||||
return config; // Success
|
||||
}
|
||||
|
||||
// Check if any of the values are still null or empty
|
||||
if (string.IsNullOrEmpty(botToken) || string.IsNullOrEmpty(tag)
|
||||
|| string.IsNullOrEmpty(channelNumId) || string.IsNullOrEmpty(adminNumId))
|
||||
{
|
||||
return null; // Failure if any are missing
|
||||
}
|
||||
|
||||
// If all required variables are present, assign to the config
|
||||
config config = new config
|
||||
{
|
||||
DB_NAME = dbName,
|
||||
MONGODB_CONNECTION_STRING = mongoDbConnectionString,
|
||||
BOT_TOKEN = botToken,
|
||||
TAG = tag,
|
||||
CHANNEL_NUMID = channelNumId,
|
||||
ADMIN_NUMID = adminNumId,
|
||||
INSTANCE = instance
|
||||
};
|
||||
|
||||
return config; // Success
|
||||
}
|
||||
|
||||
public class config
|
||||
{
|
||||
public string DB_NAME { get; set; }
|
||||
public string BOT_TOKEN { get; set; }
|
||||
public string TAG { get; set; }
|
||||
public string CHANNEL_NUMID { get; set; }
|
||||
public string ADMIN_NUMID { get; set; }
|
||||
public string INSTANCE { get; set; }
|
||||
public string MONGODB_CONNECTION_STRING { get; set; }
|
||||
}
|
||||
public class config
|
||||
{
|
||||
public string DB_NAME { get; set; }
|
||||
public string BOT_TOKEN { get; set; }
|
||||
public string TAG { get; set; }
|
||||
public string CHANNEL_NUMID { get; set; }
|
||||
public string ADMIN_NUMID { get; set; }
|
||||
public string INSTANCE { get; set; }
|
||||
public string MONGODB_CONNECTION_STRING { get; set; }
|
||||
}
|
||||
}
|
@@ -4,52 +4,36 @@ using MongoDB.Bson.Serialization.Attributes;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
|
||||
namespace mstdnCats.Models
|
||||
namespace mstdnCats.Models;
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class Post
|
||||
{
|
||||
[BsonIgnoreExtraElements]
|
||||
public class Post
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public required string mstdnPostId { get; set; }
|
||||
[JsonPropertyName("url")]
|
||||
public required string Url { get; set; }
|
||||
[JsonPropertyName("account")]
|
||||
public required Account Account { get; set; }
|
||||
[JsonPropertyName("media_attachments")]
|
||||
public required List<MediaAttachment> MediaAttachments { get; set; }
|
||||
}
|
||||
[JsonPropertyName("id")] public required string mstdnPostId { get; set; }
|
||||
[JsonPropertyName("url")] public required string Url { get; set; }
|
||||
[JsonPropertyName("account")] public required Account Account { get; set; }
|
||||
|
||||
public class Account
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public required string AccId { get; set; }
|
||||
[JsonPropertyName("username")]
|
||||
public required string Username { get; set; }
|
||||
[JsonPropertyName("acct")]
|
||||
public required string Acct { get; set; }
|
||||
[JsonPropertyName("display_name")]
|
||||
public required string DisplayName { get; set; }
|
||||
[JsonPropertyName("bot")]
|
||||
public required Boolean IsBot { get; set; }
|
||||
[JsonPropertyName("url")]
|
||||
public required string Url { get; set; }
|
||||
[JsonPropertyName("avatar_static")]
|
||||
public required string AvatarStatic { get; set; }
|
||||
}
|
||||
[JsonPropertyName("media_attachments")]
|
||||
public required List<MediaAttachment> MediaAttachments { get; set; }
|
||||
}
|
||||
|
||||
public class MediaAttachment
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public required string MediaId { get; set; }
|
||||
[JsonPropertyName("type")]
|
||||
public required string Type { get; set; }
|
||||
[JsonPropertyName("url")]
|
||||
public required string Url { get; set; }
|
||||
[JsonPropertyName("preview_url")]
|
||||
public required string PreviewUrl { get; set; }
|
||||
[JsonPropertyName("remote_url")]
|
||||
public required string RemoteUrl { get; set; }
|
||||
public Boolean Approved { get; set; } = false;
|
||||
public class Account
|
||||
{
|
||||
[JsonPropertyName("id")] public required string AccId { get; set; }
|
||||
[JsonPropertyName("username")] public required string Username { get; set; }
|
||||
[JsonPropertyName("acct")] public required string Acct { get; set; }
|
||||
[JsonPropertyName("display_name")] public required string DisplayName { get; set; }
|
||||
[JsonPropertyName("bot")] public required bool IsBot { get; set; }
|
||||
[JsonPropertyName("url")] public required string Url { get; set; }
|
||||
[JsonPropertyName("avatar_static")] public required string AvatarStatic { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
public class MediaAttachment
|
||||
{
|
||||
[JsonPropertyName("id")] public required string MediaId { get; set; }
|
||||
[JsonPropertyName("type")] public required string Type { get; set; }
|
||||
[JsonPropertyName("url")] public required string Url { get; set; }
|
||||
[JsonPropertyName("preview_url")] public required string PreviewUrl { get; set; }
|
||||
[JsonPropertyName("remote_url")] public required string RemoteUrl { get; set; }
|
||||
public bool Approved { get; set; } = false;
|
||||
}
|
64
Program.cs
64
Program.cs
@@ -12,30 +12,21 @@ public class MastodonBot
|
||||
|
||||
private static async Task Main()
|
||||
{
|
||||
|
||||
|
||||
// Configure logging
|
||||
using var loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.AddConsole();
|
||||
});
|
||||
using var loggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
|
||||
var logger = loggerFactory.CreateLogger<MastodonBot>();
|
||||
|
||||
// Read environment variables
|
||||
var config = ConfigData.fetchData();
|
||||
if (config==null)
|
||||
if (config == null)
|
||||
{
|
||||
logger.LogCritical("Error reading environment variables, either some values are missing or no .env file was found");
|
||||
throw new Exception("Error reading environment variables, either some values are missing or no .env file was found");
|
||||
logger.LogCritical(
|
||||
"Error reading environment variables, either some values are missing or no .env file was found");
|
||||
throw new Exception(
|
||||
"Error reading environment variables, either some values are missing or no .env file was found");
|
||||
}
|
||||
|
||||
|
||||
// Setup DB
|
||||
var backupDb = await DbInitializer.SetupJsonDb(config.DB_NAME);
|
||||
if (backupDb == null)
|
||||
{
|
||||
logger.LogCritical("Unable to setup json DB");
|
||||
throw new Exception("Unable to setup json DB");
|
||||
}
|
||||
var db = await DbInitializer.SetupDb(config.MONGODB_CONNECTION_STRING, config.DB_NAME);
|
||||
logger.LogInformation("DB setup done");
|
||||
|
||||
@@ -55,40 +46,47 @@ public class MastodonBot
|
||||
{
|
||||
switch (update)
|
||||
{
|
||||
case { CallbackQuery: { } callbackQuery }: {
|
||||
if(callbackQuery.Data == "new_random"){ await HandleStartMessage.HandleStartMessageAsync(callbackQuery.Message, bot, db, logger,callbackQuery); break;}
|
||||
case { CallbackQuery: { } callbackQuery }:
|
||||
{
|
||||
if (callbackQuery.Data == "new_random")
|
||||
{
|
||||
await HandleStartMessage.HandleStartMessageAsync(callbackQuery.Message, bot, db, logger,
|
||||
callbackQuery);
|
||||
break;
|
||||
}
|
||||
|
||||
else {await HandlePostAction.HandleCallbackQuery(callbackQuery, db, bot, logger); break;}
|
||||
|
||||
else
|
||||
{
|
||||
await HandlePostAction.HandleCallbackQuery(callbackQuery, db, bot, logger);
|
||||
break;
|
||||
}
|
||||
}
|
||||
default: logger.LogInformation($"Received unhandled update {update.Type}"); break;
|
||||
};
|
||||
}
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
// Handle bot messages
|
||||
async Task OnMessage(Message message, UpdateType type)
|
||||
{
|
||||
if (message.Text == "/start" && message.Chat.Type == ChatType.Private)
|
||||
{
|
||||
await HandleStartMessage.HandleStartMessageAsync(message,bot, db, logger);
|
||||
}
|
||||
await HandleStartMessage.HandleStartMessageAsync(message, bot, db, logger);
|
||||
else if (message.Text == "/backup")
|
||||
{
|
||||
await HandleDbBackup.HandleDbBackupAsync(bot, logger, config.DB_NAME, config.ADMIN_NUMID, backupDb, db);
|
||||
}
|
||||
await HandleDbBackup.HandleDbBackupAsync(bot, logger, config.DB_NAME, config.ADMIN_NUMID, db);
|
||||
// Send a message to prompt user to send /start and recieve their cat photo only if its from a telegram user and not a channel
|
||||
else if (message.Chat.Type == ChatType.Private)
|
||||
{
|
||||
await HandleStartMessage.HandleStartMessageAsync(message, bot, db, logger);
|
||||
}
|
||||
}
|
||||
|
||||
// Set a timer to fetch and process posts every 15 minutes
|
||||
_postFetchTimer = new Timer(async _ => await RunCheck.runAsync(db, bot, config.TAG, logger, config.INSTANCE), null, TimeSpan.Zero, TimeSpan.FromMinutes(10));
|
||||
// Another timer to automatically backup the DB every 1 hour
|
||||
_backupTimer = new Timer(async _ => await HandleDbBackup.HandleDbBackupAsync(bot, logger, config.DB_NAME, config.ADMIN_NUMID, backupDb,db), null, TimeSpan.Zero, TimeSpan.FromHours(6));
|
||||
_postFetchTimer = new Timer(async _ => await RunCheck.runAsync(db, bot, config.TAG, logger, config.INSTANCE),
|
||||
null, TimeSpan.Zero, TimeSpan.FromMinutes(10));
|
||||
// Another timer to automatically backup the DB every 6 hour
|
||||
_backupTimer =
|
||||
new Timer(
|
||||
async _ => await HandleDbBackup.HandleDbBackupAsync(bot, logger, config.DB_NAME, config.ADMIN_NUMID, db), null, TimeSpan.Zero, TimeSpan.FromHours(6));
|
||||
// Keep the bot running
|
||||
await Task.Delay(-1);
|
||||
}
|
||||
|
||||
}
|
36
README.md
36
README.md
@@ -1,23 +1,26 @@
|
||||
# Disclaimer:
|
||||
|
||||
This project involves scraping public data from Mastodon timelines. While this data is generally publicly accessible, it's important to note that scraping and archiving such data may have implications, especially if it involves sensitive information or terms of service violations.
|
||||
This project involves scraping public data from Mastodon timelines. While this data is generally publicly accessible,
|
||||
it's important to note that scraping and archiving such data may have implications, especially if it involves sensitive
|
||||
information or terms of service violations.
|
||||
|
||||
I am not responsible for any misuse or unauthorized use of the scraped data. It is the user's responsibility to ensure compliance with all applicable laws, regulations, and terms of service when storing, archiving, or using the data.
|
||||
I am not responsible for any misuse or unauthorized use of the scraped data. It is the user's responsibility to ensure
|
||||
compliance with all applicable laws, regulations, and terms of service when storing, archiving, or using the data.
|
||||
|
||||
## Required Environment Variables
|
||||
|
||||
| Variable Name | Description | Default Value | Format |
|
||||
|---|---|---|---|
|
||||
| DB_NAME | Database file name | (Required) | Must not have any extension, plain text |
|
||||
| BOT_TOKEN | Telegram bot token | (Required) | Standard Telegram bot token format |
|
||||
| TAG | Mastodon timeline tag | (Required) | Text with no spaces |
|
||||
| CHANNEL_NUMID | Telegram channel number ID | (Required) | Telegram channel number ID format |
|
||||
| ADMIN_NUMID | Telegram bot admin/reviewer number ID | (Required) | Telegram user number ID format |
|
||||
| Variable Name | Description | Default Value | Format |
|
||||
|---------------|---------------------------------------|---------------|-----------------------------------------|
|
||||
| DB_NAME | Database file name | (Required) | Must not have any extension, plain text |
|
||||
| BOT_TOKEN | Telegram bot token | (Required) | Standard Telegram bot token format |
|
||||
| TAG | Mastodon timeline tag | (Required) | Text with no spaces |
|
||||
| CHANNEL_NUMID | Telegram channel number ID | (Required) | Telegram channel number ID format |
|
||||
| ADMIN_NUMID | Telegram bot admin/reviewer number ID | (Required) | Telegram user number ID format |
|
||||
|
||||
## Optional Environment Variables
|
||||
|
||||
| Variable Name | Description | Default Value | Format |
|
||||
|---|---|---|---|
|
||||
| Variable Name | Description | Default Value | Format |
|
||||
|-----------------|------------------------------|--------------------------------------------|------------|
|
||||
| CUSTOM_INSTANCE | Custom Mastodon instance URL | [https://haminoa.net](https://haminoa.net) | URL format |
|
||||
|
||||
**Note:** All environment variables except `CUSTOM_INSTANCE` are required for the project to function.
|
||||
@@ -26,14 +29,17 @@ I am not responsible for any misuse or unauthorized use of the scraped data. It
|
||||
|
||||
### Published Executable
|
||||
|
||||
1. Download the published executable for `Linux-x86` from the [pipeline artifacts](https://gitlab.com/api/v4/projects/61685511/jobs/artifacts/main/raw/publish.tar.gz?job=build)
|
||||
1. Download the published executable for `Linux-x86` from
|
||||
the [pipeline artifacts](https://gitlab.com/api/v4/projects/61685511/jobs/artifacts/main/raw/publish.tar.gz?job=build)
|
||||
2. Navigate to the directory containing the downloaded archive in your terminal and extract the archive.
|
||||
|
||||
**Providing Environment Variables:**
|
||||
|
||||
**Using a `.env` file:**
|
||||
|
||||
1. Create a file named .env in the root directory of your project.
|
||||
2. Add each environment variable on a separate line in the format KEY=VALUE. For example:
|
||||
|
||||
```
|
||||
DB_NAME=my_data
|
||||
BOT_TOKEN=your_telegram_bot_token
|
||||
@@ -41,10 +47,14 @@ TAG=mastodontimelinetag
|
||||
CHANNEL_NUMID=1234567890
|
||||
ADMIN_NUMID=9876543210
|
||||
```
|
||||
|
||||
1. Run the following command:
|
||||
|
||||
```bash
|
||||
dotnet run
|
||||
or
|
||||
./mstdnCats
|
||||
```
|
||||
**Remember to replace `your_telegram_bot_token`, `my_data.json`, `important_data`, `1234567890`, and `9876543210` with your actual values.**
|
||||
|
||||
**Remember to replace `your_telegram_bot_token`, `my_data.json`, `important_data`, `1234567890`, and `9876543210` with
|
||||
your actual values.**
|
@@ -1,48 +1,24 @@
|
||||
using JsonFlatFileDataStore;
|
||||
using MongoDB.Driver;
|
||||
using mstdnCats.Models;
|
||||
|
||||
namespace mstdnCats.Services
|
||||
namespace mstdnCats.Services;
|
||||
|
||||
public class DbInitializer
|
||||
{
|
||||
public class DbInitializer
|
||||
|
||||
public static Task<IMongoCollection<Post>> SetupDb(string mongoDbConnectionString, string dbName)
|
||||
{
|
||||
public static Task<IDocumentCollection<Post>> SetupJsonDb(string _dbname)
|
||||
if (mongoDbConnectionString == null) throw new Exception("MongoDb connection string is null");
|
||||
|
||||
try
|
||||
{
|
||||
// Setup DB
|
||||
IDocumentCollection<Post>? collection = null;
|
||||
|
||||
try
|
||||
{
|
||||
// Initialize Backup DB
|
||||
var store = new DataStore($"./data/{_dbname + "_BK"}.json", minifyJson: true);
|
||||
collection = store.GetCollection<Post>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Task.FromResult<IDocumentCollection<Post>>(null);
|
||||
}
|
||||
|
||||
// Return collection
|
||||
return Task.FromResult(collection);
|
||||
var client = new MongoClient(mongoDbConnectionString);
|
||||
var database = client.GetDatabase(dbName).GetCollection<Post>("posts");
|
||||
return Task.FromResult(database);
|
||||
}
|
||||
|
||||
public static Task<IMongoCollection<Post>> SetupDb(string mongoDbConnectionString, string dbName)
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (mongoDbConnectionString == null)
|
||||
{
|
||||
throw new Exception("MongoDb connection string is null");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var client = new MongoClient(mongoDbConnectionString);
|
||||
var database = client.GetDatabase(dbName).GetCollection<Post>("posts");
|
||||
return Task.FromResult(database);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("Error while connecting to MongoDB: " + ex.Message);
|
||||
}
|
||||
throw new Exception("Error while connecting to MongoDB: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
using JsonFlatFileDataStore;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
using mstdnCats.Models;
|
||||
using Telegram.Bot;
|
||||
@@ -10,25 +11,20 @@ namespace mstdnCats.Services;
|
||||
|
||||
public class HandleDbBackup
|
||||
{
|
||||
public static async Task HandleDbBackupAsync(TelegramBotClient _bot, ILogger<MastodonBot>? logger, string dbname, string adminId,IDocumentCollection<Post> _bkDb,IMongoCollection<Post> _db)
|
||||
public static async Task HandleDbBackupAsync(TelegramBotClient _bot, ILogger<MastodonBot>? logger, string dbname,
|
||||
string adminId, IMongoCollection<Post> _db)
|
||||
{
|
||||
logger?.LogInformation("Backup requested");
|
||||
|
||||
// Retrieve all posts from DB (Exclude _id field from mongoDB since it is not needed nor implemented in Post model)
|
||||
var posts = _db.AsQueryable().ToList();
|
||||
// Retrieve all existing posts in backup DB
|
||||
var existingPosts = _bkDb.AsQueryable().ToList();
|
||||
// Insert new posts that are not in backup DB (First saves all the new ones in a list and then inserts them all at once)
|
||||
var newPosts = posts.Where(x => !existingPosts.Any(y => y.mstdnPostId == x.mstdnPostId)).ToList();
|
||||
await _bkDb.InsertManyAsync(newPosts);
|
||||
|
||||
|
||||
await using Stream stream = System.IO.File.OpenRead("./data/" + dbname+"_BK.json");
|
||||
var message = await _bot.SendDocument(adminId, document: InputFile.FromStream(stream, dbname+"_BK.json"),
|
||||
caption: "Backup of " + dbname + "\nCreated at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss" + "\nCurrent post count: " + _db.AsQueryable().Count()), parseMode: ParseMode.Html);
|
||||
var json = _db.Find(new BsonDocument()).ToList().ToJson();
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
var stream = new MemoryStream(bytes);
|
||||
|
||||
await _bot.SendDocument(adminId, InputFile.FromStream(stream, "backup.json"),
|
||||
"Backup of your collection\nCreated at " +
|
||||
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss" + "\nCurrent post count: " + _db.CountDocuments(new BsonDocument())),
|
||||
ParseMode.Html);
|
||||
logger?.LogInformation("Backup sent");
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -1,6 +1,4 @@
|
||||
using CatsOfMastodonBot.Models;
|
||||
using JsonFlatFileDataStore;
|
||||
using Microsoft.AspNetCore.Mvc.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoDB.Driver;
|
||||
using mstdnCats.Models;
|
||||
@@ -9,118 +7,118 @@ using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
|
||||
namespace mstdnCats.Services
|
||||
namespace mstdnCats.Services;
|
||||
|
||||
public class HandlePostAction
|
||||
{
|
||||
public class HandlePostAction
|
||||
public static async Task HandleCallbackQuery(CallbackQuery callbackQuery, IMongoCollection<Post> _db,
|
||||
TelegramBotClient _bot, ILogger<MastodonBot>? logger)
|
||||
{
|
||||
public static async Task HandleCallbackQuery(CallbackQuery callbackQuery, IMongoCollection<Post> _db, TelegramBotClient _bot, ILogger<MastodonBot>? logger)
|
||||
var config = ConfigData.fetchData();
|
||||
|
||||
// Extract media ID from callback query data
|
||||
string[] parts = callbackQuery.Data.Split('-');
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
var config = ConfigData.fetchData();
|
||||
logger?.LogError("Invalid callback query data format.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract media ID from callback query data
|
||||
string[] parts = callbackQuery.Data.Split('-');
|
||||
if (parts.Length != 2)
|
||||
var action = parts[0];
|
||||
var mediaId = parts[1];
|
||||
|
||||
var filter = Builders<Post>.Filter.Eq("MediaAttachments.MediaId", mediaId);
|
||||
var post = await _db.Find(filter).FirstOrDefaultAsync();
|
||||
|
||||
if (post == null)
|
||||
{
|
||||
logger?.LogInformation("No matching post found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Approve the media attachment
|
||||
if (action == "approve")
|
||||
{
|
||||
var mediaAttachment = post.MediaAttachments.FirstOrDefault(m => m.MediaId == mediaId);
|
||||
if (mediaAttachment != null)
|
||||
{
|
||||
logger?.LogError("Invalid callback query data format.");
|
||||
return;
|
||||
}
|
||||
|
||||
string action = parts[0];
|
||||
string mediaId = parts[1];
|
||||
|
||||
var filter = Builders<Post>.Filter.Eq("MediaAttachments.MediaId", mediaId);
|
||||
var post = await _db.Find(filter).FirstOrDefaultAsync();
|
||||
|
||||
if (post == null)
|
||||
{
|
||||
logger?.LogInformation("No matching post found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Approve the media attachment
|
||||
if (action == "approve")
|
||||
{
|
||||
var mediaAttachment = post.MediaAttachments.FirstOrDefault(m => m.MediaId == mediaId);
|
||||
if (mediaAttachment != null)
|
||||
// Check if the media attachment is already approved
|
||||
if (mediaAttachment.Approved)
|
||||
{
|
||||
// Check if the media attachment is already approved
|
||||
if (mediaAttachment.Approved)
|
||||
{
|
||||
await _bot.AnswerCallbackQuery(callbackQuery.Id, "Media attachment is already approved.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the media attachment
|
||||
var update = Builders<Post>.Update.Set("MediaAttachments.$.Approved", true);
|
||||
var result = await _db.UpdateOneAsync(filter, update);
|
||||
|
||||
if (result.ModifiedCount > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Send the media attachment to the channel
|
||||
var allMediaAttachments = post.MediaAttachments.ToList();
|
||||
await _bot.SendPhoto(config.CHANNEL_NUMID,
|
||||
allMediaAttachments.First(m => m.MediaId == mediaId).Url,
|
||||
caption: $"Post from " + $"<a href=\"" + post.Account.Url + "\">" +
|
||||
post.Account.DisplayName + " </a>", parseMode: ParseMode.Html
|
||||
, replyMarkup: new InlineKeyboardMarkup(InlineKeyboardButton.WithUrl("View on Mastodon", post.Url)));
|
||||
|
||||
await _bot.AnswerCallbackQuery(callbackQuery.Id,
|
||||
"Media attachment approved and sent to channel.");
|
||||
await _bot.DeleteMessage(callbackQuery.Message.Chat.Id, callbackQuery.Message.Id);
|
||||
|
||||
logger?.LogTrace($"Media attachment {mediaId} approved.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger?.LogError($"Error while sending image to the channel:{e}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger?.LogError($"Failed to update the media attachment {mediaId}. Record might not exist or was not found.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger?.LogError($"No media attachment found with MediaId {mediaId}.");
|
||||
}
|
||||
}
|
||||
|
||||
// Reject the media attachment
|
||||
else if (action == "reject")
|
||||
{
|
||||
// Check if the post has only one attachment, if so, do not delete it, else delete the associated attachment
|
||||
if (post.MediaAttachments.Count == 1 && post.MediaAttachments.First().MediaId == mediaId)
|
||||
{
|
||||
await _bot.AnswerCallbackQuery(callbackQuery.Id, "Post has only one attachment. No deletion performed.");
|
||||
await _bot.AnswerCallbackQuery(callbackQuery.Id, "Media attachment is already approved.", true);
|
||||
await _bot.DeleteMessage(callbackQuery.Message.Chat.Id, callbackQuery.Message.Id);
|
||||
|
||||
logger?.LogTrace($"Post {post.mstdnPostId} has only one attachment. No deletion performed.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var update = Builders<Post>.Update.PullFilter("MediaAttachments", Builders<MediaAttachment>.Filter.Eq("MediaId", mediaId));
|
||||
var result = await _db.UpdateOneAsync(filter, update);
|
||||
|
||||
if (result.ModifiedCount > 0)
|
||||
// Update the media attachment
|
||||
var update = Builders<Post>.Update.Set("MediaAttachments.$.Approved", true);
|
||||
var result = await _db.UpdateOneAsync(filter, update);
|
||||
|
||||
if (result.ModifiedCount > 0)
|
||||
try
|
||||
{
|
||||
await _bot.AnswerCallbackQuery(callbackQuery.Id, "Media attachment rejected.");
|
||||
// Send the media attachment to the channel
|
||||
var allMediaAttachments = post.MediaAttachments.ToList();
|
||||
await _bot.SendPhoto(config.CHANNEL_NUMID,
|
||||
allMediaAttachments.First(m => m.MediaId == mediaId).Url,
|
||||
$"Post from " + $"<a href=\"" + post.Account.Url + "\">" +
|
||||
post.Account.DisplayName + " </a>", ParseMode.Html
|
||||
, replyMarkup: new InlineKeyboardMarkup(InlineKeyboardButton.WithUrl("View on Mastodon", post.Url)));
|
||||
|
||||
await _bot.AnswerCallbackQuery(callbackQuery.Id,
|
||||
"Media attachment approved and sent to channel.");
|
||||
await _bot.DeleteMessage(callbackQuery.Message.Chat.Id, callbackQuery.Message.Id);
|
||||
|
||||
logger?.LogTrace($"Media attachment {mediaId} removed from post {post.mstdnPostId}.");
|
||||
logger?.LogTrace($"Media attachment {mediaId} approved.");
|
||||
}
|
||||
else
|
||||
catch (Exception e)
|
||||
{
|
||||
logger?.LogError($"Failed to remove media attachment {mediaId} from post {post.mstdnPostId}.");
|
||||
logger?.LogError($"Error while sending image to the channel:{e}");
|
||||
}
|
||||
}
|
||||
else
|
||||
logger?.LogError(
|
||||
$"Failed to update the media attachment {mediaId}. Record might not exist or was not found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger?.LogError("Invalid action specified.");
|
||||
logger?.LogError($"No media attachment found with MediaId {mediaId}.");
|
||||
}
|
||||
}
|
||||
|
||||
// Reject the media attachment
|
||||
else if (action == "reject")
|
||||
{
|
||||
// Check if the post has only one attachment, if so, do not delete it, else delete the associated attachment
|
||||
if (post.MediaAttachments.Count == 1 && post.MediaAttachments.First().MediaId == mediaId)
|
||||
{
|
||||
await _bot.AnswerCallbackQuery(callbackQuery.Id,
|
||||
"Post has only one attachment. No deletion performed.");
|
||||
await _bot.DeleteMessage(callbackQuery.Message.Chat.Id, callbackQuery.Message.Id);
|
||||
|
||||
logger?.LogTrace($"Post {post.mstdnPostId} has only one attachment. No deletion performed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var update = Builders<Post>.Update.PullFilter("MediaAttachments",
|
||||
Builders<MediaAttachment>.Filter.Eq("MediaId", mediaId));
|
||||
var result = await _db.UpdateOneAsync(filter, update);
|
||||
|
||||
if (result.ModifiedCount > 0)
|
||||
{
|
||||
await _bot.AnswerCallbackQuery(callbackQuery.Id, "Media attachment rejected.");
|
||||
await _bot.DeleteMessage(callbackQuery.Message.Chat.Id, callbackQuery.Message.Id);
|
||||
|
||||
logger?.LogTrace($"Media attachment {mediaId} removed from post {post.mstdnPostId}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger?.LogError($"Failed to remove media attachment {mediaId} from post {post.mstdnPostId}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger?.LogError("Invalid action specified.");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,42 +1,41 @@
|
||||
using JsonFlatFileDataStore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
using mstdnCats.Models;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
|
||||
namespace CatsOfMastodonBot.Services
|
||||
namespace CatsOfMastodonBot.Services;
|
||||
|
||||
public class HandleStartMessage
|
||||
{
|
||||
public class HandleStartMessage
|
||||
public static async Task HandleStartMessageAsync(Message message, TelegramBotClient _bot,
|
||||
IMongoCollection<Post> _db, ILogger<MastodonBot>? logger, CallbackQuery callbackQuery = null)
|
||||
{
|
||||
public static async Task HandleStartMessageAsync(Message message, TelegramBotClient _bot, IMongoCollection<Post> _db, ILogger<MastodonBot>? logger, CallbackQuery callbackQuery = null)
|
||||
{
|
||||
logger?.LogInformation("Start message received, trigger source: " + (callbackQuery != null ? "Callback" : "Start command"));
|
||||
logger?.LogInformation("Start message received, trigger source: " +
|
||||
(callbackQuery != null ? "Callback" : "Start command"));
|
||||
|
||||
// choose all media attachments that are approved
|
||||
var mediaAttachmentsToSelect =await _db.AsQueryable()
|
||||
.Where(post => post.MediaAttachments.Any(media => media.Approved))
|
||||
.ToListAsync();
|
||||
// select random approved media attachment
|
||||
var selectedMediaAttachment = mediaAttachmentsToSelect[new Random().Next(mediaAttachmentsToSelect.Count)];
|
||||
// send media attachment
|
||||
await _bot.SendPhoto(message.Chat.Id, selectedMediaAttachment.MediaAttachments.FirstOrDefault(m => m.Approved == true).Url,
|
||||
caption: $"Here is your cat!🐈\n" + "<a href=\"" + selectedMediaAttachment.Url + "\">" + $"View on Mastodon " + " </a>", parseMode: ParseMode.Html
|
||||
, replyMarkup: new InlineKeyboardMarkup().AddButton(InlineKeyboardButton.WithUrl("Join channel 😺", "https://t.me/catsofmastodon"))
|
||||
.AddNewRow()
|
||||
.AddButton(InlineKeyboardButton.WithCallbackData("Send me another one!", $"new_random")));
|
||||
// choose all media attachments that are approved
|
||||
var mediaAttachmentsToSelect = _db.AsQueryable()
|
||||
.Where(post => post.MediaAttachments.Any(media => media.Approved))
|
||||
.ToList();
|
||||
|
||||
// answer callback query from "send me another cat" button
|
||||
if (callbackQuery != null)
|
||||
{
|
||||
await _bot.AnswerCallbackQuery(callbackQuery.Id, "Catch your cat! 😺");
|
||||
}
|
||||
logger?.LogInformation("Random cat sent!");
|
||||
// select random approved media attachment
|
||||
var selectedMediaAttachment = mediaAttachmentsToSelect[new Random().Next(mediaAttachmentsToSelect.Count)];
|
||||
// send media attachment
|
||||
await _bot.SendPhoto(message.Chat.Id,
|
||||
selectedMediaAttachment.MediaAttachments.FirstOrDefault(m => m.Approved == true).Url,
|
||||
$"Here is your cat!🐈\n" + "<a href=\"" + selectedMediaAttachment.Url + "\">" +
|
||||
$"View on Mastodon " + " </a>", ParseMode.Html
|
||||
, replyMarkup: new InlineKeyboardMarkup()
|
||||
.AddButton(InlineKeyboardButton.WithUrl("Join channel 😺", "https://t.me/catsofmastodon"))
|
||||
.AddNewRow()
|
||||
.AddButton(InlineKeyboardButton.WithCallbackData("Send me another one!", $"new_random")));
|
||||
|
||||
// answer callback query from "send me another cat" button
|
||||
if (callbackQuery != null) await _bot.AnswerCallbackQuery(callbackQuery.Id, "Catch your cat! 😺");
|
||||
|
||||
}
|
||||
logger?.LogInformation("Random cat sent!");
|
||||
}
|
||||
}
|
@@ -2,40 +2,39 @@ using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using mstdnCats.Models;
|
||||
|
||||
namespace mstdnCats.Services
|
||||
namespace mstdnCats.Services;
|
||||
|
||||
public sealed class PostResolver
|
||||
{
|
||||
public sealed class PostResolver
|
||||
public static async Task<List<Post>?> GetPostsAsync(string tag, ILogger<MastodonBot>? logger, string instance)
|
||||
{
|
||||
public static async Task<List<Post>?> GetPostsAsync(string tag, ILogger<MastodonBot>? logger, string instance)
|
||||
// Get posts
|
||||
var _httpClient = new HttpClient();
|
||||
// Get posts from mastodon api (40 latest posts)
|
||||
var requestUrl = $"{instance}/api/v1/timelines/tag/{tag}?limit=40";
|
||||
var response = await _httpClient.GetAsync(requestUrl);
|
||||
|
||||
// Print out ratelimit info
|
||||
logger?.LogInformation("Remaining requests: " +
|
||||
response.Headers.GetValues("X-RateLimit-Remaining").First() + "time to reset: " +
|
||||
response.Headers.GetValues("X-RateLimit-Reset").First());
|
||||
|
||||
// Check if response is ok
|
||||
if (
|
||||
response.StatusCode == System.Net.HttpStatusCode.OK ||
|
||||
response.Content.Headers.ContentType.MediaType.Contains("application/json") ||
|
||||
(response.Headers.TryGetValues("X-RateLimit-Remaining", out var remaining) &&
|
||||
int.Parse(remaining.First()) != 0)
|
||||
)
|
||||
{
|
||||
// Get posts
|
||||
HttpClient _httpClient = new HttpClient();
|
||||
// Get posts from mastodon api (40 latest posts)
|
||||
string requestUrl = $"{instance}/api/v1/timelines/tag/{tag}?limit=40";
|
||||
var response = await _httpClient.GetAsync(requestUrl);
|
||||
// Deserialize response based on 'Post' model
|
||||
return JsonSerializer.Deserialize<List<Post>>(await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
// Print out ratelimit info
|
||||
logger?.LogInformation("Remaining requests: " +
|
||||
response.Headers.GetValues("X-RateLimit-Remaining").First() + "time to reset: " +
|
||||
response.Headers.GetValues("X-RateLimit-Reset").First());
|
||||
|
||||
// Check if response is ok
|
||||
if (
|
||||
response.StatusCode == System.Net.HttpStatusCode.OK ||
|
||||
response.Content.Headers.ContentType.MediaType.Contains("application/json") ||
|
||||
response.Headers.TryGetValues("X-RateLimit-Remaining", out var remaining) &&
|
||||
int.Parse(remaining.First()) != 0
|
||||
)
|
||||
{
|
||||
// Deserialize response based on 'Post' model
|
||||
return JsonSerializer.Deserialize<List<Post>>(await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
logger?.LogCritical("Error while getting posts: " + response.StatusCode);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger?.LogCritical("Error while getting posts: " + response.StatusCode);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
using CatsOfMastodonBot.Models;
|
||||
using JsonFlatFileDataStore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoDB.Driver;
|
||||
using mstdnCats.Models;
|
||||
@@ -7,55 +6,53 @@ using Telegram.Bot;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
|
||||
namespace mstdnCats.Services
|
||||
namespace mstdnCats.Services;
|
||||
|
||||
public class ProcessPosts
|
||||
{
|
||||
public class ProcessPosts
|
||||
public static async Task<List<MediaAttachment>> checkAndInsertPostsAsync(IMongoCollection<Post> _db,
|
||||
TelegramBotClient _bot, List<Post> fetchedPosts, ILogger<MastodonBot>? logger)
|
||||
{
|
||||
public static async Task<List<MediaAttachment>> checkAndInsertPostsAsync(IMongoCollection<Post> _db, TelegramBotClient _bot, List<Post> fetchedPosts, ILogger<MastodonBot>? logger)
|
||||
{
|
||||
var config = ConfigData.fetchData();
|
||||
var config = ConfigData.fetchData();
|
||||
|
||||
// Get existing posts
|
||||
var existingPosts = _db.AsQueryable().Select(x => x.mstdnPostId).ToArray();
|
||||
logger?.LogInformation($"Recieved posts to proccess: {fetchedPosts.Count} - total existing posts: {existingPosts.Length}");
|
||||
int newPosts = 0;
|
||||
// Process posts
|
||||
foreach (Post post in fetchedPosts)
|
||||
// Get existing posts
|
||||
var existingPosts = _db.AsQueryable().Select(x => x.mstdnPostId).ToArray();
|
||||
logger?.LogInformation(
|
||||
$"Recieved posts to proccess: {fetchedPosts.Count} - total existing posts: {existingPosts.Length}");
|
||||
var newPosts = 0;
|
||||
// Process posts
|
||||
List<Post> validPosts = new();
|
||||
foreach (var post in fetchedPosts)
|
||||
// Check if post already exists
|
||||
if (!existingPosts.Contains(post.mstdnPostId) && post.MediaAttachments.Count > 0 &&
|
||||
post.Account.IsBot == false)
|
||||
{
|
||||
// Check if post already exists
|
||||
if (!existingPosts.Contains(post.mstdnPostId) && post.MediaAttachments.Count > 0 && post.Account.IsBot == false)
|
||||
{
|
||||
|
||||
|
||||
// Send approve or reject message to admin
|
||||
foreach (var media in post.MediaAttachments)
|
||||
{
|
||||
if (media.Type == "image")
|
||||
// Send approve or reject message to admin
|
||||
foreach (var media in post.MediaAttachments)
|
||||
if (media.Type == "image")
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
await _bot.SendPhoto(config.ADMIN_NUMID, media.PreviewUrl, caption: $"<a href=\"" + post.Url + "\"> Mastodon </a>", parseMode: ParseMode.Html
|
||||
, replyMarkup: new InlineKeyboardMarkup().AddButton("Approve", $"approve-{media.MediaId}").AddButton("Reject", $"reject-{media.MediaId}"));
|
||||
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
logger?.LogError("Error while sending message to admin: " + ex.Message + " - Media URL: " + media.PreviewUrl);
|
||||
}
|
||||
await _bot.SendPhoto(config.ADMIN_NUMID, media.PreviewUrl,
|
||||
$"<a href=\"" + post.Url + "\"> Mastodon </a>", ParseMode.Html
|
||||
, replyMarkup: new InlineKeyboardMarkup().AddButton("Approve", $"approve-{media.MediaId}").AddButton("Reject", $"reject-{media.MediaId}"));
|
||||
validPosts.Add(post);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger?.LogError("Error while sending message to admin: " + ex.Message + " - Media URL: " +
|
||||
media.PreviewUrl);
|
||||
}
|
||||
}
|
||||
// Insert post
|
||||
await _db.InsertOneAsync(post);
|
||||
newPosts++;
|
||||
|
||||
}
|
||||
newPosts++;
|
||||
}
|
||||
|
||||
logger?.LogInformation($"Proccesing done, stats: received {fetchedPosts.Count} posts, inserted and sent {newPosts} new posts.");
|
||||
// Insert post
|
||||
await _db.InsertManyAsync(validPosts);
|
||||
logger?.LogInformation(
|
||||
$"Proccesing done, stats: received {fetchedPosts.Count} posts, inserted and sent {newPosts} new posts.");
|
||||
|
||||
// Return list of media attachments
|
||||
var alldbpostsattachmentlist = _db.AsQueryable().SelectMany(x => x.MediaAttachments).ToList();
|
||||
return alldbpostsattachmentlist;
|
||||
}
|
||||
// Return list of media attachments
|
||||
var alldbpostsattachmentlist = _db.AsQueryable().SelectMany(x => x.MediaAttachments).ToList();
|
||||
return alldbpostsattachmentlist;
|
||||
}
|
||||
}
|
@@ -1,34 +1,31 @@
|
||||
using JsonFlatFileDataStore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoDB.Driver;
|
||||
using mstdnCats.Models;
|
||||
using Telegram.Bot;
|
||||
|
||||
namespace mstdnCats.Services
|
||||
namespace mstdnCats.Services;
|
||||
|
||||
public class RunCheck
|
||||
{
|
||||
public class RunCheck
|
||||
public static async Task<bool> runAsync(IMongoCollection<Post> _db, TelegramBotClient _bot, string _tag,
|
||||
ILogger<MastodonBot>? logger, string _instance)
|
||||
{
|
||||
public static async Task<bool> runAsync(IMongoCollection<Post> _db, TelegramBotClient _bot, string _tag, ILogger<MastodonBot>? logger, string _instance)
|
||||
// Run check
|
||||
try
|
||||
{
|
||||
// Run check
|
||||
try
|
||||
{
|
||||
// First get posts
|
||||
var posts = await PostResolver.GetPostsAsync(_tag, logger, _instance);
|
||||
// First get posts
|
||||
var posts = await PostResolver.GetPostsAsync(_tag, logger, _instance);
|
||||
|
||||
if (posts == null)
|
||||
{
|
||||
logger?.LogCritical("Unable to get posts");
|
||||
}
|
||||
if (posts == null) logger?.LogCritical("Unable to get posts");
|
||||
|
||||
// Then process them
|
||||
await ProcessPosts.checkAndInsertPostsAsync(_db, _bot, posts, logger);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger?.LogCritical("Error while running check: " + ex.Message);
|
||||
}
|
||||
return true;
|
||||
// Then process them
|
||||
await ProcessPosts.checkAndInsertPostsAsync(_db, _bot, posts, logger);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger?.LogCritical("Error while running check: " + ex.Message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -1,19 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DotNetEnv" Version="3.1.1" />
|
||||
<PackageReference Include="JsonFlatFileDataStore" Version="2.4.2" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="3.1.0" />
|
||||
<PackageReference Include="Telegram.Bot" Version="22.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DotNetEnv" Version="3.1.1" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="3.1.0" />
|
||||
<PackageReference Include="Telegram.Bot" Version="22.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
Reference in New Issue
Block a user