Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Database/BaseDBMigration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ CREATE TABLE Contacts (
ContactId INTEGER NOT NULL,
status TEXT,
MutedUntil TEXT,
DisplayName TEXT,
PRIMARY KEY (UserId, ContactId)
)");
}
Expand Down
2 changes: 2 additions & 0 deletions Database/Interfaces/IContactRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public interface IContactRemover
public interface IContactSetter
{
void SetContactStatus(long SenderTelegramID, long AccepterTelegramID, string status);
bool SetContactDisplayName(int userId, int contactId, string? displayName);
}

public interface IContactGetter
Expand All @@ -39,4 +40,5 @@ public interface IContactGetter
DateTime? GetMutedUntil(int userId, int contactId);
int GetContactIDByLink(string link);
int GetContactByTelegramID(long telegramID);
string? GetContactDisplayName(int userId, int contactId);
}
32 changes: 32 additions & 0 deletions Database/Migrations/AddContactDisplayName.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (C) 2024-2025 ZenonEl
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Эта программа является свободным программным обеспечением: вы можете распространять и/или изменять
// её на условиях Стандартной общественной лицензии GNU Affero, опубликованной
// Фондом свободного программного обеспечения, либо версии 3 лицензии, либо
// (по вашему выбору) любой более поздней версии.

using FluentMigrator;

namespace TelegramMediaRelayBot.Database.Migrations;

[Migration(20260329)]
public class AddContactDisplayName : Migration
{
public override void Up()
{
if (!Schema.Table("Contacts").Column("DisplayName").Exists())
{
Alter.Table("Contacts")
.AddColumn("DisplayName").AsString(255).Nullable();
}
}

public override void Down()
{
Delete.Column("DisplayName").FromTable("Contacts");
}
}
48 changes: 44 additions & 4 deletions Database/Repositories/SqLite/Contacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,18 +292,18 @@ public class SqliteContactSetter(string connectionString) : IContactSetter
public void SetContactStatus(long SenderTelegramID, long AccepterTelegramID, string status)
{
const string query = @"
UPDATE Contacts
SET Status = @Status
UPDATE Contacts
SET Status = @Status
WHERE UserId = @UserId AND ContactId = @ContactId";

SqliteContactGetter contactGetter = new(_connectionString);
SqliteUserGetter userGetter = new(_connectionString);

using (var connection = new SqliteConnection(_connectionString))
{
try
{
connection.Execute(query, new
connection.Execute(query, new
{
Status = status,
UserId = userGetter.GetUserIDbyTelegramID(SenderTelegramID),
Expand All @@ -316,6 +316,28 @@ UPDATE Contacts
}
}
}

public bool SetContactDisplayName(int userId, int contactId, string? displayName)
{
const string query = @"
UPDATE Contacts
SET DisplayName = @displayName
WHERE UserId = @userId AND ContactId = @contactId";

using (var connection = new SqliteConnection(_connectionString))
{
try
{
int affected = connection.Execute(query, new { userId, contactId, displayName });
return affected > 0;
}
catch (Exception ex)
{
Log.Error("Error editing database: " + ex.Message);
return false;
}
}
}
}

public class SqliteContactGetter(string connectionString) : IContactGetter
Expand Down Expand Up @@ -434,4 +456,22 @@ public int GetContactByTelegramID(long telegramID)
return -1;
}
}

public string? GetContactDisplayName(int userId, int contactId)
{
const string query = @"
SELECT DisplayName
FROM Contacts
WHERE UserId = @userId AND ContactId = @contactId";
try
{
using var connection = new SqliteConnection(_connectionString);
return connection.QueryFirstOrDefault<string?>(query, new { userId, contactId });
}
catch (Exception ex)
{
Log.Error(ex, "An error occurred in the method {MethodName}", nameof(GetContactDisplayName));
return null;
}
}
}
17 changes: 17 additions & 0 deletions Resources/texts.resx
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,23 @@ Please specify the group ID for work:</value>
<value>Specify the contact ID to delete:</value>
</data>

<!-- Rename contact -->
<data name="RenameContactInstructions" xml:space="preserve">
<value>To rename a contact, provide their ID or link.</value>
</data>
<data name="InputNewDisplayName" xml:space="preserve">
<value>Enter a new display name for this contact:</value>
</data>
<data name="ResetDisplayNameButtonText" xml:space="preserve">
<value>Reset to original</value>
</data>
<data name="DisplayNameSet" xml:space="preserve">
<value>Display name changed to: {0}</value>
</data>
<data name="DisplayNameReset" xml:space="preserve">
<value>Display name has been reset to the original.</value>
</data>

<!-- Mutes and unmutes -->
<data name="MuteUserInstructions" xml:space="preserve">
<value>To mute a person (you won't receive videos from them), you need to provide either their ID or their link.</value>
Expand Down
17 changes: 17 additions & 0 deletions Resources/texts.ru-RU.resx
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,23 @@ ID: {0}
<value>Укажите ID контактов для удаления:</value>
</data>

<!-- Переименование контакта -->
<data name="RenameContactInstructions" xml:space="preserve">
<value>Чтобы переименовать контакт, укажите его ID или ссылку.</value>
</data>
<data name="InputNewDisplayName" xml:space="preserve">
<value>Введите новое отображаемое имя для этого контакта:</value>
</data>
<data name="ResetDisplayNameButtonText" xml:space="preserve">
<value>Сбросить к оригиналу</value>
</data>
<data name="DisplayNameSet" xml:space="preserve">
<value>Отображаемое имя изменено на: {0}</value>
</data>
<data name="DisplayNameReset" xml:space="preserve">
<value>Отображаемое имя сброшено к оригиналу.</value>
</data>

<!-- Муты и размуты -->
<data name="MuteUserInstructions" xml:space="preserve">
<value>Чтобы замутить человека (вы не будете получать от него медиа) вам нужно указать либо его ID либо его ссылку</value>
Expand Down
1 change: 1 addition & 0 deletions TelegramBot/Handlers/ICallBackQuery/CallbackNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public static class CallbackNames
public const string ViewContacts = "view_contacts";
public const string MuteContact = "mute_contact";
public const string UnmuteContact = "unmute_contact";
public const string RenameContact = "edit_contact_name";
public const string DeleteContact = "delete_contact";

// Settings
Expand Down
25 changes: 25 additions & 0 deletions TelegramBot/Handlers/ICallBackQuery/IContactsCallbackQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,31 @@ public async Task ExecuteAsync(Update update, ITelegramBotClient botClient, Canc
}
}

public class RenameContactCommand : IBotCallbackQueryHandlers
{
private readonly IContactSetter _contactSetterRepository;
private readonly IContactGetter _contactGetterRepository;
private readonly IUserGetter _userGetter;

public RenameContactCommand(
IContactSetter contactSetterRepository,
IContactGetter contactGetterRepository,
IUserGetter userGetter)
{
_contactSetterRepository = contactSetterRepository;
_contactGetterRepository = contactGetterRepository;
_userGetter = userGetter;
}

public string Name => "edit_contact_name";

public async Task ExecuteAsync(Update update, ITelegramBotClient botClient, CancellationToken ct)
{
long chatId = update.CallbackQuery!.Message!.Chat.Id;
await Contacts.RenameContact(botClient, update, chatId, _contactSetterRepository, _contactGetterRepository, _userGetter);
}
}

public class DeleteContactCommand : IBotCallbackQueryHandlers
{
private readonly IContactRemover _contactRemoverRepository;
Expand Down
12 changes: 12 additions & 0 deletions TelegramBot/Menu/Contacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ public static async Task ViewContacts(ITelegramBotClient botClient, Update updat
await CommonUtilities.SendMessage(botClient, update, KeyboardUtils.GetViewContactsKeyboardMarkup(), cancellationToken, $"{Config.GetResourceString("YourContacts")}\n{string.Join("\n", contactUsersInfo)}");
}

public static async Task RenameContact(
ITelegramBotClient botClient,
Update update,
long chatId,
IContactSetter contactSetterRepository,
IContactGetter contactGetterRepository,
IUserGetter userGetter)
{
await botClient.SendMessage(update.CallbackQuery!.Message!.Chat.Id, Config.GetResourceString("RenameContactInstructions"), cancellationToken: cancellationToken);
UserSessionManager.Set(chatId, new ProcessRenameContactState(contactSetterRepository, contactGetterRepository, userGetter));
}

public static async Task EditContactGroup(
ITelegramBotClient botClient,
Update update,
Expand Down
121 changes: 121 additions & 0 deletions TelegramBot/States/RenameContact.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright (C) 2024-2025 ZenonEl
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Эта программа является свободным программным обеспечением: вы можете распространять и/или изменять
// её на условиях Стандартной общественной лицензии GNU Affero, опубликованной
// Фондом свободного программного обеспечения, либо версии 3 лицензии, либо
// (по вашему выбору) любой более поздней версии.

using TelegramMediaRelayBot.Database.Interfaces;
using TelegramMediaRelayBot.TelegramBot.Utils;


namespace TelegramMediaRelayBot;

public class ProcessRenameContactState : IUserState
{
public UserRenameContactState currentState;

private int userId { get; set; }
private int targetContactId { get; set; }
private readonly IContactSetter _contactSetter;
private readonly IContactGetter _contactGetter;
private readonly IUserGetter _userGetter;

public ProcessRenameContactState(
IContactSetter contactSetter,
IContactGetter contactGetter,
IUserGetter userGetter
)
{
currentState = UserRenameContactState.WaitingForLinkOrID;
_contactSetter = contactSetter;
_contactGetter = contactGetter;
_userGetter = userGetter;
}

public string GetCurrentState()
{
return currentState.ToString();
}

public async Task ProcessState(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
{
long chatId = CommonUtilities.GetIDfromUpdate(update);
if (CommonUtilities.CheckNonZeroID(chatId)) return;

if (!UserSessionManager.TryGetValue(chatId, out IUserState? value))
{
return;
}

var userState = (ProcessRenameContactState)value;

Check warning on line 55 in TelegramBot/States/RenameContact.cs

View workflow job for this annotation

GitHub Actions / build_and_run

Converting null literal or possible null value to non-nullable type.

switch (userState.currentState)

Check warning on line 57 in TelegramBot/States/RenameContact.cs

View workflow job for this annotation

GitHub Actions / build_and_run

Dereference of a possibly null reference.
{
case UserRenameContactState.WaitingForLinkOrID:
int contactId;
if (int.TryParse(update.Message!.Text, out contactId))
{
List<long> allowedIds = await _contactGetter.GetAllContactUserTGIds(_userGetter.GetUserIDbyTelegramID(update.Message.Chat.Id));
string name = _userGetter.GetUserNameByID(contactId);
if (name == "" || !allowedIds.Contains(_userGetter.GetTelegramIDbyUserID(contactId)))
{
await CommonUtilities.AlertMessageAndShowMenu(botClient, update, chatId, Config.GetResourceString("NoUserFoundByID"));
return;
}
await botClient.SendMessage(chatId, string.Format(Config.GetResourceString("WillWorkWithContact"), contactId, name), cancellationToken: cancellationToken,
replyMarkup: ReplyKeyboardUtils.GetSingleButtonKeyboardMarkup(Config.GetResourceString("NextButtonText")));
}
else
{
string link = update.Message.Text!;
contactId = _contactGetter.GetContactIDByLink(link);
List<long> allowedIds = await _contactGetter.GetAllContactUserTGIds(_userGetter.GetUserIDbyTelegramID(update.Message.Chat.Id));

if (contactId == -1 || !allowedIds.Contains(_userGetter.GetTelegramIDbyUserID(contactId)))
{
await CommonUtilities.AlertMessageAndShowMenu(botClient, update, chatId, Config.GetResourceString("NoUserFoundByLink"));
return;
}
string name = _userGetter.GetUserNameByID(contactId);
await botClient.SendMessage(chatId, string.Format(Config.GetResourceString("WillWorkWithContact"), contactId, name), cancellationToken: cancellationToken);
}
userState.userId = _userGetter.GetUserIDbyTelegramID(chatId);
userState.targetContactId = contactId;
await botClient.SendMessage(chatId, Config.GetResourceString("InputNewDisplayName"), cancellationToken: cancellationToken,
replyMarkup: ReplyKeyboardUtils.GetSingleButtonKeyboardMarkup(Config.GetResourceString("ResetDisplayNameButtonText")));
userState.currentState = UserRenameContactState.WaitingForNewName;
break;

case UserRenameContactState.WaitingForNewName:
if (await CommonUtilities.HandleStateBreakCommand(botClient, update, chatId)) return;

string newName = update.Message!.Text!;
string? displayName = newName.Equals(Config.GetResourceString("ResetDisplayNameButtonText"), StringComparison.OrdinalIgnoreCase)
? null
: newName;

await ReplyKeyboardUtils.RemoveReplyMarkup(botClient, chatId, cancellationToken);

bool success = _contactSetter.SetContactDisplayName(userState.userId, userState.targetContactId, displayName);
UserSessionManager.Remove(chatId);

if (success)
{
string resultText = displayName != null
? string.Format(Config.GetResourceString("DisplayNameSet"), displayName)
: Config.GetResourceString("DisplayNameReset");
await CommonUtilities.AlertMessageAndShowMenu(botClient, update, chatId, resultText);
}
else
{
await CommonUtilities.AlertMessageAndShowMenu(botClient, update, chatId, Config.GetResourceString("ActionCancelledError"));
}
break;
}
}
}
7 changes: 7 additions & 0 deletions TelegramBot/States/States.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ public enum UserInboundState
Finish
}

public enum UserRenameContactState
{
WaitingForLinkOrID,
WaitingForNewName,
Finish
}

public enum UsersStandardState
{
ProcessAction,
Expand Down
Loading