OSDN Git Service

RAD-480: Text from Muted/Blocked Users no longer shows in group chats
[radegast/radegast.git] / Radegast / Core / IMTextManager.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2014, Radegast Development Team
4 // All rights reserved.
5 // 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 
9 //     * Redistributions of source code must retain the above copyright notice,
10 //       this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //     * Neither the name of the application "Radegast", nor the names of its
15 //       contributors may be used to endorse or promote products derived from
16 //       this software without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 // $Id$
30 //
31 using System;
32 using System.Collections;
33 using System.Drawing;
34 using System.Text;
35 using System.IO;
36 using Radegast.Netcom;
37 using OpenMetaverse;
38 using OpenMetaverse.StructuredData;
39
40 namespace Radegast
41 {
42     public class IMTextManager
43     {
44         public bool DingOnAllIncoming = false;
45
46         RadegastInstance instance;
47         RadegastNetcom netcom { get { return instance.Netcom; } }
48         ITextPrinter textPrinter;
49         IMTextManagerType Type;
50         UUID sessionID;
51         string sessionName;
52         bool AutoResponseSent = false;
53         ArrayList textBuffer;
54
55         bool showTimestamps;
56
57         public IMTextManager(RadegastInstance instance, ITextPrinter textPrinter, IMTextManagerType type, UUID sessionID, string sessionName)
58         {
59             this.sessionID = sessionID;
60             this.sessionName = sessionName;
61             this.textPrinter = textPrinter;
62             this.textBuffer = new ArrayList();
63             this.Type = type;
64
65             this.instance = instance;
66             PrintLastLog();
67             AddNetcomEvents();
68             InitializeConfig();
69
70         }
71
72         private void InitializeConfig()
73         {
74             Settings s = instance.GlobalSettings;
75
76             if (s["im_timestamps"].Type == OSDType.Unknown)
77                 s["im_timestamps"] = OSD.FromBoolean(true);
78
79             showTimestamps = s["im_timestamps"].AsBoolean();
80
81             s.OnSettingChanged += new Settings.SettingChangedCallback(s_OnSettingChanged);
82         }
83
84         void s_OnSettingChanged(object sender, SettingsEventArgs e)
85         {
86             if (e.Key == "im_timestamps" && e.Value != null)
87             {
88                 showTimestamps = e.Value.AsBoolean();
89                 ReprintAllText();
90             }
91         }
92
93         private void AddNetcomEvents()
94         {
95             netcom.InstantMessageReceived += new EventHandler<InstantMessageEventArgs>(netcom_InstantMessageReceived);
96             netcom.InstantMessageSent += new EventHandler<InstantMessageSentEventArgs>(netcom_InstantMessageSent);
97         }
98
99         private void RemoveNetcomEvents()
100         {
101             netcom.InstantMessageReceived -= netcom_InstantMessageReceived;
102             netcom.InstantMessageSent -= netcom_InstantMessageSent;
103         }
104
105         private void netcom_InstantMessageSent(object sender, InstantMessageSentEventArgs e)
106         {
107             if (e.SessionID != sessionID) return;
108
109             textBuffer.Add(e);
110             ProcessIM(e);
111         }
112
113         private void netcom_InstantMessageReceived(object sender, InstantMessageEventArgs e)
114         {
115             if (e.IM.IMSessionID != sessionID) return;
116             if (e.IM.Dialog == InstantMessageDialog.StartTyping ||
117                 e.IM.Dialog == InstantMessageDialog.StopTyping)
118                 return;
119
120             if (instance.Client.Self.MuteList.Find(me => me.Type == MuteType.Resident && me.ID == e.IM.FromAgentID) != null)
121             {
122                 return;
123             }
124
125             textBuffer.Add(e);
126             ProcessIM(e);
127         }
128
129         public void ProcessIM(object e)
130         {
131             if (e is InstantMessageEventArgs)
132                 this.ProcessIncomingIM((InstantMessageEventArgs)e);
133             else if (e is InstantMessageSentEventArgs)
134                 this.ProcessOutgoingIM((InstantMessageSentEventArgs)e);
135         }
136
137         private void ProcessOutgoingIM(InstantMessageSentEventArgs e)
138         {
139             PrintIM(e.Timestamp, netcom.LoginOptions.FullName, instance.Client.Self.AgentID, e.Message);
140         }
141
142         private void ProcessIncomingIM(InstantMessageEventArgs e)
143         {
144             string msg = e.IM.Message;
145
146             if (instance.RLV.RestictionActive("recvim", e.IM.FromAgentID.ToString()))
147             {
148                 msg = "*** IM blocked by your viewer";
149
150                 if (Type == IMTextManagerType.Agent)
151                 {
152                     instance.Client.Self.InstantMessage(instance.Client.Self.Name,
153                             e.IM.FromAgentID,
154                             "***  The Resident you messaged is prevented from reading your instant messages at the moment, please try again later.",
155                             e.IM.IMSessionID,
156                             InstantMessageDialog.BusyAutoResponse,
157                             InstantMessageOnline.Offline,
158                             instance.Client.Self.RelativePosition,
159                             instance.Client.Network.CurrentSim.ID,
160                             null);
161                 }
162             }
163
164             if (DingOnAllIncoming)
165             {
166                 instance.MediaManager.PlayUISound(UISounds.IM);
167             }
168             PrintIM(DateTime.Now, instance.Names.Get(e.IM.FromAgentID, e.IM.FromAgentName), e.IM.FromAgentID, msg);
169
170             if (!AutoResponseSent && Type == IMTextManagerType.Agent && e.IM.FromAgentID != UUID.Zero && e.IM.FromAgentName != "Second Life")
171             {
172                 bool autoRespond = false;
173                 AutoResponseType art = (AutoResponseType)instance.GlobalSettings["auto_response_type"].AsInteger();
174
175                 switch (art)
176                 {
177                     case AutoResponseType.WhenBusy: autoRespond = instance.State.IsBusy; break;
178                     case AutoResponseType.WhenFromNonFriend: autoRespond = !instance.Client.Friends.FriendList.ContainsKey(e.IM.FromAgentID); break;
179                     case AutoResponseType.Always: autoRespond = true; break;
180                 }
181
182                 if (autoRespond)
183                 {
184                     AutoResponseSent = true;
185                     instance.Client.Self.InstantMessage(instance.Client.Self.Name,
186                         e.IM.FromAgentID,
187                         instance.GlobalSettings["auto_response_text"].AsString(),
188                         e.IM.IMSessionID,
189                         InstantMessageDialog.BusyAutoResponse,
190                         InstantMessageOnline.Online,
191                         instance.Client.Self.RelativePosition,
192                         instance.Client.Network.CurrentSim.ID,
193                         null);
194
195                     PrintIM(DateTime.Now, instance.Client.Self.Name, instance.Client.Self.AgentID, instance.GlobalSettings["auto_response_text"].AsString());
196                 }
197             }
198         }
199
200         public void DisplayNotification(string message)
201         {
202             if (instance.MainForm.InvokeRequired)
203             {
204                 instance.MainForm.Invoke(new System.Windows.Forms.MethodInvoker(() => DisplayNotification(message)));
205                 return;
206             }
207
208             if (showTimestamps)
209             {
210                 textPrinter.ForeColor = SystemColors.GrayText;
211                 textPrinter.PrintText(DateTime.Now.ToString("[HH:mm] "));
212             }
213
214             textPrinter.ForeColor = Color.DarkCyan;
215             instance.LogClientMessage(sessionName + ".txt", message);
216             textPrinter.PrintTextLine(message);
217         }
218
219         private void PrintIM(DateTime timestamp, string fromName, UUID fromID, string message)
220         {
221             if (showTimestamps)
222             {
223                 textPrinter.ForeColor = SystemColors.GrayText;
224                 textPrinter.PrintText(timestamp.ToString("[HH:mm] "));
225             }
226
227             textPrinter.ForeColor = SystemColors.WindowText;
228
229             if (instance.GlobalSettings["av_name_link"])
230             {
231                 textPrinter.InsertLink(fromName, string.Format("secondlife:///app/agent/{0}/about", fromID));
232             }
233             else
234             {
235                 textPrinter.PrintText(fromName);
236             }
237
238             StringBuilder sb = new StringBuilder();
239
240             if (message.StartsWith("/me "))
241             {
242                 sb.Append(message.Substring(3));
243             }
244             else
245             {
246                 sb.Append(": ");
247                 sb.Append(message);
248             }
249
250             instance.LogClientMessage(sessionName + ".txt", fromName + sb.ToString());
251             textPrinter.PrintTextLine(sb.ToString());
252         }
253
254         public static string ReadEndTokens(string path, Int64 numberOfTokens, Encoding encoding, string tokenSeparator)
255         {
256
257             int sizeOfChar = encoding.GetByteCount("\n");
258             byte[] buffer = encoding.GetBytes(tokenSeparator);
259
260
261             using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
262             {
263                 Int64 tokenCount = 0;
264                 Int64 endPosition = fs.Length / sizeOfChar;
265
266                 for (Int64 position = sizeOfChar; position < endPosition; position += sizeOfChar)
267                 {
268                     fs.Seek(-position, SeekOrigin.End);
269                     fs.Read(buffer, 0, buffer.Length);
270
271                     if (encoding.GetString(buffer) == tokenSeparator)
272                     {
273                         tokenCount++;
274                         if (tokenCount == numberOfTokens)
275                         {
276                             byte[] returnBuffer = new byte[fs.Length - fs.Position];
277                             fs.Read(returnBuffer, 0, returnBuffer.Length);
278                             return encoding.GetString(returnBuffer);
279                         }
280                     }
281                 }
282
283                 // handle case where number of tokens in file is less than numberOfTokens
284                 fs.Seek(0, SeekOrigin.Begin);
285                 buffer = new byte[fs.Length];
286                 fs.Read(buffer, 0, buffer.Length);
287                 return encoding.GetString(buffer);
288             }
289         }
290
291         private void PrintLastLog()
292         {
293             string last = string.Empty;
294             try
295             {
296                 last = IMTextManager.ReadEndTokens(instance.ChatFileName(sessionName + ".txt"), 20, Encoding.UTF8, Environment.NewLine);
297             }
298             catch { }
299
300             if (string.IsNullOrEmpty(last))
301             {
302                 return;
303             }
304
305             string[] lines = last.Split(Environment.NewLine.ToCharArray());
306             for (int i = 0; i < lines.Length; i++)
307             {
308                 string msg = lines[i].Trim();
309                 if (!string.IsNullOrEmpty(msg))
310                 {
311                     textPrinter.PrintTextLine(msg, SystemColors.GrayText);
312                 }
313             }
314
315             textPrinter.PrintTextLine("====", SystemColors.GrayText);
316         }
317
318         public void ReprintAllText()
319         {
320             textPrinter.ClearText();
321             PrintLastLog();
322             foreach (object obj in textBuffer)
323                 ProcessIM(obj);
324         }
325
326         public void ClearInternalBuffer()
327         {
328             textBuffer.Clear();
329         }
330
331         public void CleanUp()
332         {
333             RemoveNetcomEvents();
334
335             textBuffer.Clear();
336             textBuffer = null;
337
338             textPrinter = null;
339         }
340
341         public ITextPrinter TextPrinter
342         {
343             get { return textPrinter; }
344             set { textPrinter = value; }
345         }
346
347         public bool ShowTimestamps
348         {
349             get { return showTimestamps; }
350             set { showTimestamps = value; }
351         }
352
353         public UUID SessionID
354         {
355             get { return sessionID; }
356             set { sessionID = value; }
357         }
358     }
359
360     public enum IMTextManagerType
361     {
362         Agent,
363         Group,
364         Conference
365     }
366 }