2019-12-17 06:04:55 +00:00
|
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
|
|
|
|
|
|
using System;
|
2019-12-17 05:59:27 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using osu.Framework.Allocation;
|
|
|
|
|
using osu.Framework.Bindables;
|
|
|
|
|
using osu.Framework.Graphics;
|
|
|
|
|
using osu.Framework.Graphics.Sprites;
|
2019-12-26 02:32:40 +00:00
|
|
|
|
using osu.Framework.Logging;
|
2019-12-17 05:59:27 +00:00
|
|
|
|
using osu.Game.Configuration;
|
|
|
|
|
using osu.Game.Graphics;
|
|
|
|
|
using osu.Game.Online.API;
|
|
|
|
|
using osu.Game.Overlays;
|
|
|
|
|
using osu.Game.Overlays.Notifications;
|
|
|
|
|
using osu.Game.Users;
|
|
|
|
|
|
|
|
|
|
namespace osu.Game.Online.Chat
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Component that handles creating and posting notifications for incoming messages.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class MessageNotifier : Component
|
|
|
|
|
{
|
|
|
|
|
[Resolved(CanBeNull = true)]
|
|
|
|
|
private NotificationOverlay notificationOverlay { get; set; }
|
|
|
|
|
|
|
|
|
|
[Resolved(CanBeNull = true)]
|
|
|
|
|
private ChatOverlay chatOverlay { get; set; }
|
|
|
|
|
|
|
|
|
|
[Resolved(CanBeNull = true)]
|
|
|
|
|
private ChannelManager channelManager { get; set; }
|
|
|
|
|
|
|
|
|
|
private Bindable<bool> notifyOnMention;
|
|
|
|
|
private Bindable<bool> notifyOnChat;
|
|
|
|
|
private Bindable<User> localUser;
|
2020-01-25 16:03:39 +00:00
|
|
|
|
private readonly BindableList<Channel> joinedChannels = new BindableList<Channel>();
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
|
|
|
|
[BackgroundDependencyLoader]
|
2019-12-26 02:32:40 +00:00
|
|
|
|
private void load(OsuConfigManager config, IAPIProvider api)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
|
|
|
|
notifyOnMention = config.GetBindable<bool>(OsuSetting.ChatHighlightName);
|
|
|
|
|
notifyOnChat = config.GetBindable<bool>(OsuSetting.ChatMessageNotification);
|
|
|
|
|
localUser = api.LocalUser;
|
2019-12-26 02:32:40 +00:00
|
|
|
|
|
2020-01-25 15:43:51 +00:00
|
|
|
|
channelManager.JoinedChannels.BindTo(joinedChannels);
|
|
|
|
|
|
2019-12-26 02:32:40 +00:00
|
|
|
|
// Listen for new messages
|
2020-01-25 15:43:51 +00:00
|
|
|
|
joinedChannels.ItemsAdded += joinedChannels =>
|
2019-12-26 02:32:40 +00:00
|
|
|
|
{
|
|
|
|
|
foreach (var channel in joinedChannels)
|
|
|
|
|
channel.NewMessagesArrived += channel_NewMessagesArrived;
|
|
|
|
|
};
|
|
|
|
|
|
2020-01-25 15:43:51 +00:00
|
|
|
|
joinedChannels.ItemsRemoved += leftChannels =>
|
2019-12-26 02:32:40 +00:00
|
|
|
|
{
|
|
|
|
|
foreach (var channel in leftChannels)
|
|
|
|
|
channel.NewMessagesArrived -= channel_NewMessagesArrived;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-16 22:15:30 +00:00
|
|
|
|
private void channel_NewMessagesArrived(IEnumerable<Message> messages)
|
2019-12-26 02:32:40 +00:00
|
|
|
|
{
|
|
|
|
|
if (messages == null || !messages.Any())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
HandleMessages(messages.First().ChannelId, messages);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void HandleMessages(long channelId, IEnumerable<Message> messages)
|
|
|
|
|
{
|
2020-01-21 23:29:12 +00:00
|
|
|
|
var channel = channelManager.JoinedChannels.SingleOrDefault(c => c.Id == channelId);
|
2019-12-26 02:32:40 +00:00
|
|
|
|
|
|
|
|
|
if (channel == null)
|
|
|
|
|
{
|
|
|
|
|
Logger.Log($"Couldn't resolve channel id {channelId}", LoggingTarget.Information);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HandleMessages(channel, messages);
|
2019-12-17 05:59:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void HandleMessages(Channel channel, IEnumerable<Message> messages)
|
|
|
|
|
{
|
2020-01-21 23:13:07 +00:00
|
|
|
|
// Only send notifications, if ChatOverlay and the target channel aren't visible.
|
2020-01-22 09:47:51 +00:00
|
|
|
|
if (chatOverlay?.IsPresent == true && channelManager.CurrentChannel.Value == channel)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2020-01-21 23:27:46 +00:00
|
|
|
|
foreach (var message in messages.OrderByDescending(m => m.Id))
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
2020-01-16 23:00:10 +00:00
|
|
|
|
// ignore messages that already have been read
|
2020-01-21 23:28:08 +00:00
|
|
|
|
if (message.Id <= channel.LastReadId)
|
2020-01-16 23:00:10 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2020-01-21 22:42:15 +00:00
|
|
|
|
if (message.Sender.Id == localUser.Value.Id)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
2020-01-21 23:41:46 +00:00
|
|
|
|
// check for private messages first, if true, skip checking mentions to prevent duplicate notifications about the same message.
|
2020-01-19 16:55:17 +00:00
|
|
|
|
if (checkForPMs(channel, message))
|
|
|
|
|
continue;
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
2020-01-19 16:55:17 +00:00
|
|
|
|
// change output to bool again if another "message processor" is added.
|
2020-01-21 22:42:15 +00:00
|
|
|
|
checkForMentions(channel, message, localUser.Value.Username);
|
2020-01-19 16:55:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-18 13:17:26 +00:00
|
|
|
|
|
2020-01-19 16:55:17 +00:00
|
|
|
|
private bool checkForPMs(Channel channel, Message message)
|
|
|
|
|
{
|
|
|
|
|
if (!notifyOnChat.Value || channel.Type != ChannelType.PM)
|
|
|
|
|
return false;
|
2020-01-16 22:15:30 +00:00
|
|
|
|
|
2020-01-22 09:48:55 +00:00
|
|
|
|
var notification = new PrivateMessageNotification(message.Sender.Username, channel);
|
2020-01-21 23:28:59 +00:00
|
|
|
|
|
2020-01-22 09:48:55 +00:00
|
|
|
|
notificationOverlay?.Post(notification);
|
2020-01-19 16:55:17 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void checkForMentions(Channel channel, Message message, string username)
|
|
|
|
|
{
|
2020-01-25 15:43:51 +00:00
|
|
|
|
if (!notifyOnMention.Value || !IsMentioning(message.Content, username))
|
2020-01-19 16:55:17 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var notification = new MentionNotification(message.Sender.Username, channel);
|
|
|
|
|
notificationOverlay?.Post(notification);
|
2019-12-17 05:59:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-01-25 13:40:53 +00:00
|
|
|
|
/// Checks if <paramref name="message"/> contains <paramref name="username"/>, if not, retries making spaces into underscores.
|
2019-12-17 05:59:27 +00:00
|
|
|
|
/// </summary>
|
2020-01-25 13:40:53 +00:00
|
|
|
|
/// <returns>If the <paramref name="message"/> mentions the <paramref name="username"/></returns>
|
2020-01-25 15:44:45 +00:00
|
|
|
|
public bool IsMentioning(string message, string username)
|
2020-01-25 13:40:53 +00:00
|
|
|
|
{
|
|
|
|
|
// sanitize input to handle casing
|
|
|
|
|
message = message.ToLower();
|
|
|
|
|
username = username.ToLower();
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
2020-01-25 13:40:53 +00:00
|
|
|
|
return message.Contains(username) || message.Contains(username.Replace(' ', '_'));
|
|
|
|
|
}
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
2020-01-16 23:00:10 +00:00
|
|
|
|
public class PrivateMessageNotification : SimpleNotification
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
2020-01-22 09:48:55 +00:00
|
|
|
|
public PrivateMessageNotification(string username, Channel channel)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
|
|
|
|
Icon = FontAwesome.Solid.Envelope;
|
|
|
|
|
Username = username;
|
2020-01-19 16:55:17 +00:00
|
|
|
|
Channel = channel;
|
2020-01-22 09:48:55 +00:00
|
|
|
|
Text = $"You received a private message from '{Username}'. Click to read it!";
|
2019-12-17 05:59:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-22 10:23:27 +00:00
|
|
|
|
public string Username { get; }
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
2020-01-22 10:23:27 +00:00
|
|
|
|
public Channel Channel { get; }
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
2020-01-21 23:28:59 +00:00
|
|
|
|
public Action<PrivateMessageNotification> OnRemove { get; set; }
|
|
|
|
|
|
2019-12-17 05:59:27 +00:00
|
|
|
|
public override bool IsImportant => false;
|
|
|
|
|
|
|
|
|
|
[BackgroundDependencyLoader]
|
2020-01-21 23:28:59 +00:00
|
|
|
|
private void load(OsuColour colours, ChatOverlay chatOverlay, NotificationOverlay notificationOverlay, ChannelManager channelManager)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
|
|
|
|
IconBackgound.Colour = colours.PurpleDark;
|
2020-01-21 23:28:59 +00:00
|
|
|
|
|
2019-12-17 05:59:27 +00:00
|
|
|
|
Activated = delegate
|
|
|
|
|
{
|
2020-01-19 16:55:17 +00:00
|
|
|
|
notificationOverlay.Hide();
|
|
|
|
|
chatOverlay.Show();
|
|
|
|
|
channelManager.CurrentChannel.Value = Channel;
|
2020-01-16 23:00:10 +00:00
|
|
|
|
|
2019-12-17 05:59:27 +00:00
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-16 23:00:10 +00:00
|
|
|
|
public class MentionNotification : SimpleNotification
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
2020-01-19 16:55:17 +00:00
|
|
|
|
public MentionNotification(string username, Channel channel)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
|
|
|
|
Icon = FontAwesome.Solid.At;
|
|
|
|
|
Text = $"Your name was mentioned in chat by '{username}'. Click to find out why!";
|
2020-01-19 16:55:17 +00:00
|
|
|
|
Channel = channel;
|
2019-12-17 05:59:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-22 10:23:27 +00:00
|
|
|
|
public Channel Channel { get; }
|
2019-12-17 05:59:27 +00:00
|
|
|
|
|
|
|
|
|
public override bool IsImportant => false;
|
|
|
|
|
|
|
|
|
|
[BackgroundDependencyLoader]
|
2020-01-19 16:55:17 +00:00
|
|
|
|
private void load(OsuColour colours, ChatOverlay chatOverlay, NotificationOverlay notificationOverlay, ChannelManager channelManager)
|
2019-12-17 05:59:27 +00:00
|
|
|
|
{
|
|
|
|
|
IconBackgound.Colour = colours.PurpleDark;
|
2020-01-21 23:28:59 +00:00
|
|
|
|
|
2019-12-17 05:59:27 +00:00
|
|
|
|
Activated = delegate
|
|
|
|
|
{
|
2020-01-19 16:55:17 +00:00
|
|
|
|
notificationOverlay.Hide();
|
|
|
|
|
chatOverlay.Show();
|
|
|
|
|
channelManager.CurrentChannel.Value = Channel;
|
|
|
|
|
|
2019-12-17 05:59:27 +00:00
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|