Skip to content

Commit

Permalink
feat: add repo chatbot basic
Browse files Browse the repository at this point in the history
  • Loading branch information
mtai0524 committed Nov 26, 2024
1 parent 2c78d14 commit e3da892
Show file tree
Hide file tree
Showing 14 changed files with 279 additions and 45 deletions.
97 changes: 97 additions & 0 deletions NotaionWebApp/Notaion.Application/Hubs/ChatHub.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;

namespace Notaion.Application.Hubs
{
public class ChatHub : Hub
{
//public async Task SendFriendRequest(string receiverConnectionId, string senderId, string receiverId, string senderName)
//{
// await Clients.Client(receiverConnectionId).SendAsync("ReceiveFriendRequest", senderId, receiverId, senderName);
//}

public async Task SendNotification(string userId, string message)
{
await Clients.User(userId).SendAsync("ReceiveNotification", message);
}
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}

public async Task SendMessagePrivate(string sender, string reveiver, string message, string currentUsername, string friendUsername)
{
await Clients.All.SendAsync("ReceiveMessagePrivate", sender, reveiver, message, currentUsername, friendUsername);
}

//console

public async Task SendMessageToGroup(string groupName, string user, string message)
{
await Clients.Group(groupName).SendAsync("ReceiveMessage", user, message);
}

public async Task JoinGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("ReceiveMessage", "System", $"{Context.ConnectionId} has joined the group {groupName}.");
}

public async Task LeaveGroup(string groupName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("ReceiveMessage", "System", $"{Context.ConnectionId} has left the group {groupName}.");
}
public async Task CreateGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("ReceiveMessage", "System", $"{Context.ConnectionId} has created and joined the group {groupName}.");
}


private static ConcurrentDictionary<string, (string UserId, string UserName, string Avatar)> OnlineUsers = new ConcurrentDictionary<string, (string UserId, string UserName, string Avatar)>();



public async Task RegisterUser(RegisterUserModel user) // react call RegisterUser with invoke
{
OnlineUsers.TryAdd(Context.ConnectionId, (user.UserId, user.UserName, user.Avatar));

var userList = OnlineUsers.Values.Select(u => new { u.UserId, u.UserName, u.Avatar }).ToList();

await Clients.All.SendAsync("ReceiveOnlineUsers", userList);
}



public override async Task OnDisconnectedAsync(Exception? exception)
{
if (OnlineUsers.TryRemove(Context.ConnectionId, out var user))
{
await Clients.All.SendAsync("UserDisconnected", user.UserId);
}

await base.OnDisconnectedAsync(exception);
}

public async Task LogoutUser(string userId) // react call LogoutUser with invoke
{
// find user by userId
var userConnectionId = OnlineUsers.FirstOrDefault(x => x.Value.UserId == userId).Key;

if (userConnectionId != null)
{
OnlineUsers.TryRemove(userConnectionId, out var removedUser);

await Clients.All.SendAsync("UserDisconnected", removedUser.UserId);
}
}

}
public class RegisterUserModel
{
public string UserId { get; set; }

Check warning on line 93 in NotaionWebApp/Notaion.Application/Hubs/ChatHub.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'UserId' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
public string UserName { get; set; }

Check warning on line 94 in NotaionWebApp/Notaion.Application/Hubs/ChatHub.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'UserName' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
public string Avatar { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public interface IChatService
Task<List<ChatResponseDto>> GetChatsAsync();
Task<List<ChatResponseDto>> GetChatsHiddenAsync();
Task<ChatResponseDto> CreateChatAsync(CreateChatDto chatDto);
Task<ChatResponseDto> CreateChatbotAsync(CreateChatDto chatDto);
Task<int> HideChatAllAsync();
}
}
3 changes: 2 additions & 1 deletion NotaionWebApp/Notaion.Application/Notaion.Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="5.0.17" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Notaion.Domain\Notaion.Domain.csproj" />
<ProjectReference Include="..\Notaion.Domain\Notaion.Domain.csproj" />
</ItemGroup>

</Project>
63 changes: 54 additions & 9 deletions NotaionWebApp/Notaion.Application/Services/ChatService.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using AutoMapper;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Notaion.Application.Common.Helpers;
using Notaion.Application.DTOs.Chats;
using Notaion.Application.Hubs;
using Notaion.Application.Interfaces.Services;
using Notaion.Domain.Entities;
using Notaion.Domain.Interfaces;
Expand All @@ -19,12 +21,40 @@ public class ChatService : IChatService
private readonly IChatRepository _chatRepository;
private readonly IMapper _mapper;
private readonly IUnitOfWork _unitOfWork;
public ChatService(IUnitOfWork unitOfWork, IGenericRepository<Chat> chatGenericRepository, IMapper mapper, IChatRepository chatRepository)
private readonly IHubContext<ChatHub> _hubContext;
public ChatService(IUnitOfWork unitOfWork, IGenericRepository<Chat> chatGenericRepository, IMapper mapper, IChatRepository chatRepository, IHubContext<ChatHub> hubContext)
{
_chatGenericRepository = chatGenericRepository;
_mapper = mapper;
_chatRepository = chatRepository;
_unitOfWork = unitOfWork;
_hubContext = hubContext;
}

public async Task<ChatResponseDto> CreateChatbotAsync(CreateChatDto chatDto)
{
await _unitOfWork.BeginTransactionAsync();

try
{
var chat = _mapper.Map<Chat>(chatDto);

var createdChatbot = await _unitOfWork.ChatRepository.AddChatbotAsync(chat);

await _unitOfWork.SaveChangeAsync();

var response = _mapper.Map<ChatResponseDto>(createdChatbot);

await _unitOfWork.CommitTransactionAsync(); // không có commit thì hủy tất cả thay đổi (sau này làm chat ẩn danh)

return response;
}
catch (Exception)
{
await _unitOfWork.RollbackTransactionAsync();
throw;
}

}

public async Task<ChatResponseDto> CreateChatAsync(CreateChatDto chatDto)
Expand All @@ -33,17 +63,31 @@ public async Task<ChatResponseDto> CreateChatAsync(CreateChatDto chatDto)
{
throw new ArgumentException("Invalid chat message.");
}
await _unitOfWork.BeginTransactionAsync();

try
{
var chat = _mapper.Map<Chat>(chatDto);
chat.SentDate = DateTimeHelper.GetVietnamTime();

var createdChat = await _unitOfWork.ChatRepository.AddAsync(chat);

var chat = _mapper.Map<Chat>(chatDto);
chat.SentDate = DateTimeHelper.GetVietnamTime();
await _unitOfWork.SaveChangeAsync();

var createdChat = await _chatGenericRepository.AddAsync(chat);
var response = _mapper.Map<ChatResponseDto>(createdChat);

var response = _mapper.Map<ChatResponseDto>(createdChat);
await _unitOfWork.CommitTransactionAsync();

return response;
return response;
}
catch
{
await _unitOfWork.RollbackTransactionAsync();
throw;
}
}


public async Task<int> HideChatAllAsync()
{
await _unitOfWork.BeginTransactionAsync();
Expand All @@ -62,7 +106,7 @@ public async Task<int> HideChatAllAsync()
}
catch
{
await _unitOfWork.RollBackAsync();
await _unitOfWork.RollbackTransactionAsync();
throw;
}
}
Expand All @@ -86,7 +130,7 @@ public async Task<List<ChatResponseDto>> GetChatsAsync()
// return _mapper.Map<List<ChatResponseDto>>(chats);
//}


/*
* generic repo
*/
Expand All @@ -103,6 +147,7 @@ public async Task<List<ChatResponseDto>> GetChatsHiddenAsync()
return _mapper.Map<List<ChatResponseDto>>(chats);
}




}
}
2 changes: 2 additions & 0 deletions NotaionWebApp/Notaion.Domain/Interfaces/IChatRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ namespace Notaion.Domain.Interfaces
{
public interface IChatRepository : IGenericRepository<Chat>
{
Task<string> GetChatbotResponseAsync(string userMessage);
Task<Chat> AddChatbotAsync(Chat chat);
}
}
3 changes: 3 additions & 0 deletions NotaionWebApp/Notaion.Domain/Interfaces/IGenericRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@ public interface IGenericRepository<T> where T : class
// modifier
Task UpdateAsync(T entity);
Task UpdateRangeAsync(IEnumerable<T> entities);

// delete
Task DeleteAsync(string id);
}
}
2 changes: 1 addition & 1 deletion NotaionWebApp/Notaion.Domain/Interfaces/IUnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ public interface IUnitOfWork : IDisposable
Task<int> SaveChangeAsync();
Task BeginTransactionAsync();
Task CommitTransactionAsync();
Task RollBackAsync();
Task RollbackTransactionAsync();
}
}
7 changes: 5 additions & 2 deletions NotaionWebApp/Notaion.Infrastructure/DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@ namespace Notaion.Infrastructure
{
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
public static void AddDbContext(this IServiceCollection services, IConfiguration configuration)
{
var connectionString = configuration.GetConnectionString("DefaultConnection");

services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(connectionString);
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}, ServiceLifetime.Scoped);
}

public static IServiceCollection AddInfrastructure(this IServiceCollection services)
{
// chat
services.AddScoped<IChatRepository, ChatRepository>();
services.AddScoped<IChatService, ChatService>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.EntityFrameworkCore;
using Notaion.Application.Common.Helpers;
using Notaion.Application.DTOs.Chats;
using Notaion.Application.Services;
using Notaion.Domain.Entities;
using Notaion.Domain.Interfaces;
using Notaion.Infrastructure.Context;
Expand All @@ -20,5 +21,76 @@ public ChatRepository(ApplicationDbContext context) : base(context)
{
_context = context;
}

public async Task<Chat> AddChatbotAsync(Chat chat)
{
var botResponse = await GetChatbotResponseAsync(chat.Content);

var botChat = new Chat
{
Content = botResponse,
UserId = "2071b4d2-734d-479b-8b64-ba4e81c30231",
UserName = "Chatbot",
SentDate = DateTimeHelper.GetVietnamTime(),
Hide = false
};

await _context.Chat.AddAsync(botChat);

return botChat;
}

public async Task<string> GetChatbotResponseAsync(string userMessage)
{
var predefinedResponses = new Dictionary<string, string>
{
{ "hello", "Chào bạn! Tôi là chatbot." },
{ "xin chào", "Chào bạn! Tôi là chatbot." },
{ "hi", "Chào bạn! Tôi là chatbot." },
{ "how are you", "Tôi là chatbot, luôn sẵn sàng hỗ trợ bạn." },
{ "how's it going", "Tôi là chatbot, luôn sẵn sàng hỗ trợ bạn." },
{ "bye", "Tạm biệt! Hẹn gặp lại." },
{ "goodbye", "Tạm biệt! Hẹn gặp lại." },
{ "what is your name", "Tôi là một chatbot không có tên riêng." },
{ "can you help me", "Tất nhiên! Bạn cần giúp đỡ về vấn đề gì?" },
{ "how do I get to the nearest coffee shop", "Bạn có thể tìm thấy quán cà phê gần nhất bằng cách sử dụng Google Maps." },
{ "tell me a joke", "Tại sao máy tính không bao giờ đói? Vì nó đã có đủ bộ nhớ!" },
{ "what time is it", "Sorry, I can't provide the time information." },
{ "what is the weather like", "I'm sorry, I can't provide current weather information." },
{ "who are you", "Tôi là một chatbot được lập trình để hỗ trợ bạn." },
{ "thank you", "You're welcome! I'm here to help!" },
{ "sorry", "Không sao, bạn cần thêm sự trợ giúp nào không?" },
{ "good morning", "Good morning! Have a great day ahead." },
{ "good night", "Chúc bạn ngủ ngon! Hẹn gặp lại vào ngày mai." },
{ "how old are you", "Tôi không có tuổi, tôi chỉ là một chương trình." },
{ "what can you do", "I can help you with answering questions, sending messages, and much more." },
{ "tell me a fact", "Did you know that the Earth rotates around the Sun at a speed of about 30 km/s?" },
{ "are you real", "Tôi không phải là con người, nhưng tôi có thể giúp bạn rất nhiều điều." },
{ "what is your favorite color", "Màu sắc yêu thích của tôi là màu xanh dương." },
{ "info", "Xin chào tôi là chatbot do minhtai training, mục đích dùng để sau khi cậu chủ không còn nữa thì tôi vẫn có thể thay thế :>" },
{ "help",
"Dưới đây là một số lệnh mà bạn có thể sử dụng:\n" +
"/shortcut - Hiển thị các phím tắt\n" +
"/hello - Chào chatbot\n" +
"/bye - Tạm biệt chatbot\n" +
"/joke - Xem một câu chuyện vui\n" +
"/fact - Nhận một thông tin thú vị" +
"/info - Xem thông tin chatbot"
},
{ "are you here", "Tôi vẫn luôn ở đây, mãi mãi không rời đi :>" },
{ "bạn có đó không", "Tôi vẫn luôn ở đây, mãi mãi không rời đi :>" },
{ "/shortcut", "alt + x : toggle drawer \n" + "alt + c : toggle chat box" },

};
foreach (var keyword in predefinedResponses.Keys)
{
if (userMessage.Contains(keyword, StringComparison.OrdinalIgnoreCase))
{
return predefinedResponses[keyword];
}
}

return "Xin chào! Bạn cần giúp gì từ chatbot?";
}
}
}
Loading

0 comments on commit e3da892

Please sign in to comment.