1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 12:07:23 +08:00
osu-lazer/osu.Game/Online/Chat/MessageNotifier.cs

213 lines
8.2 KiB
C#
Raw Normal View History

2019-12-17 14:04:55 +08: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;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
2019-12-26 10:32:40 +08:00
using osu.Framework.Logging;
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;
2020-02-07 23:50:22 +08:00
private Bindable<bool> notifyOnPM;
2021-05-28 03:58:54 +08:00
private readonly IBindable<User> localUser = new Bindable<User>();
2021-06-05 17:03:49 +08:00
private readonly IBindableList<Channel> joinedChannels = new BindableList<Channel>();
[BackgroundDependencyLoader]
2019-12-26 10:32:40 +08:00
private void load(OsuConfigManager config, IAPIProvider api)
{
notifyOnMention = config.GetBindable<bool>(OsuSetting.ChatHighlightName);
2020-02-07 23:50:22 +08:00
notifyOnPM = config.GetBindable<bool>(OsuSetting.ChatMessageNotification);
2021-06-05 17:03:49 +08:00
localUser.BindTo(api.LocalUser);
2020-01-25 23:43:51 +08:00
2019-12-26 10:32:40 +08:00
// Listen for new messages
joinedChannels.CollectionChanged += channelsChanged;
2021-06-05 17:03:49 +08:00
joinedChannels.BindTo(channelManager.JoinedChannels);
}
2019-12-26 10:32:40 +08:00
private void channelsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
2019-12-26 10:32:40 +08:00
{
case NotifyCollectionChangedAction.Add:
foreach (var channel in e.NewItems.Cast<Channel>())
channel.NewMessagesArrived += newMessagesArrived;
break;
case NotifyCollectionChangedAction.Remove:
foreach (var channel in e.OldItems.Cast<Channel>())
channel.NewMessagesArrived -= newMessagesArrived;
break;
}
2019-12-26 10:32:40 +08:00
}
2020-02-06 02:20:16 +08:00
private void newMessagesArrived(IEnumerable<Message> messages)
2019-12-26 10:32:40 +08:00
{
if (messages == null || !messages.Any())
return;
HandleMessages(messages.First().ChannelId, messages);
}
/// <summary>
/// Searches for a channel with the matching <paramref name="channelId"/>, returns <see langword="null"/> when none found.
/// </summary>
private Channel fetchJoinedChannel(long channelId)
{
return channelManager.JoinedChannels.SingleOrDefault(c => c.Id == channelId);
}
2019-12-26 10:32:40 +08:00
public void HandleMessages(long channelId, IEnumerable<Message> messages)
{
// Fetch channel object
var channel = fetchJoinedChannel(channelId);
2019-12-26 10:32:40 +08:00
if (channel == null)
{
Logger.Log($"Couldn't resolve channel id {channelId}", LoggingTarget.Information);
return;
}
2020-01-22 07:13:07 +08:00
// Only send notifications, if ChatOverlay and the target channel aren't visible.
if (chatOverlay?.IsPresent == true && channelManager.CurrentChannel.Value == channel)
return;
foreach (var message in messages.OrderByDescending(m => m.Id))
{
// ignore messages that already have been read
2020-01-22 07:28:08 +08:00
if (message.Id <= channel.LastReadId)
return;
if (message.Sender.Id == localUser.Value.Id)
continue;
2020-02-07 23:52:53 +08:00
// check for private messages first,
// to avoid both posting two notifications about the same message
2020-01-20 00:55:17 +08:00
if (checkForPMs(channel, message))
continue;
2021-06-05 21:57:14 +08:00
_ = checkForMentions(channel, message, localUser.Value.Username);
2020-01-20 00:55:17 +08:00
}
}
2021-06-05 21:57:14 +08:00
/// <summary>
/// Checks whether the user enabled private message notifications and whether specified <paramref name="message"/> is a direct message.
/// </summary>
/// <param name="channel">The channel associated to the <paramref name="message"/></param>
/// <param name="message">The message to be checked</param>
2020-01-20 00:55:17 +08:00
private bool checkForPMs(Channel channel, Message message)
{
2020-02-07 23:50:22 +08:00
if (!notifyOnPM.Value || channel.Type != ChannelType.PM)
2020-01-20 00:55:17 +08:00
return false;
2021-06-05 21:57:14 +08:00
if (channel.Id != message.ChannelId)
throw new ArgumentException("The provided channel doesn't match with the channel id provided by the message parameter.", nameof(channel));
2021-06-05 21:57:14 +08:00
var notification = new PrivateMessageNotification(message.Sender.Username, channel);
2020-01-22 17:48:55 +08:00
notificationOverlay?.Post(notification);
2020-01-20 00:55:17 +08:00
return true;
}
2021-06-05 21:57:14 +08:00
/// <summary>
/// Checks whether the user enabled mention notifications and whether specified <paramref name="message"/> mentions the provided <paramref name="username"/>.
/// </summary>
/// <param name="channel">The channel associated to the <paramref name="message"/></param>
/// <param name="message">The message to be checked</param>
/// <param name="username">The username that will be checked for</param>
private bool checkForMentions(Channel channel, Message message, string username)
2020-01-20 00:55:17 +08:00
{
2020-01-29 09:07:08 +08:00
if (!notifyOnMention.Value || !isMentioning(message.Content, username))
2021-06-05 21:57:14 +08:00
return false;
if (channel.Id != message.ChannelId)
throw new ArgumentException("The provided channel doesn't match with the channel id provided by the message parameter.", nameof(channel));
2020-01-20 00:55:17 +08:00
var notification = new MentionNotification(message.Sender.Username, channel);
notificationOverlay?.Post(notification);
2021-06-05 21:57:14 +08:00
return true;
}
/// <summary>
2020-01-25 21:40:53 +08:00
/// Checks if <paramref name="message"/> contains <paramref name="username"/>, if not, retries making spaces into underscores.
/// </summary>
2020-01-25 21:40:53 +08:00
/// <returns>If the <paramref name="message"/> mentions the <paramref name="username"/></returns>
private static bool isMentioning(string message, string username) => message.Contains(username, StringComparison.OrdinalIgnoreCase) || message.Contains(username.Replace(' ', '_'), StringComparison.OrdinalIgnoreCase);
public class OpenChannelNotification : SimpleNotification
{
public OpenChannelNotification(Channel channel)
{
2020-02-04 06:03:27 +08:00
this.channel = channel;
}
2020-02-04 06:03:27 +08:00
private readonly Channel channel;
public override bool IsImportant => false;
[BackgroundDependencyLoader]
private void load(OsuColour colours, ChatOverlay chatOverlay, NotificationOverlay notificationOverlay, ChannelManager channelManager)
{
IconBackgound.Colour = colours.PurpleDark;
Activated = delegate
{
2020-01-20 00:55:17 +08:00
notificationOverlay.Hide();
chatOverlay.Show();
2020-02-04 06:03:27 +08:00
channelManager.CurrentChannel.Value = channel;
return true;
};
}
}
public class PrivateMessageNotification : OpenChannelNotification
{
2021-05-28 03:58:54 +08:00
public PrivateMessageNotification(string username, Channel channel)
: base(channel)
{
Icon = FontAwesome.Solid.Envelope;
Text = $"You received a private message from '{username}'. Click to read it!";
}
}
public class MentionNotification : OpenChannelNotification
{
2021-05-28 03:58:54 +08:00
public MentionNotification(string username, Channel channel)
: base(channel)
{
Icon = FontAwesome.Solid.At;
Text = $"Your name was mentioned in chat by '{username}'. Click to find out why!";
}
}
}
}