using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Text; using System.Xml; using System.Xml.Serialization; using Misuzilla.Net.Irc; namespace Misuzilla.Applications.TwitterIrcGateway { /// /// Groupをチャンネル名で格納します。 /// public class Groups : SortedList { public Groups() : base(StringComparer.InvariantCultureIgnoreCase) { } private static Object _syncObject = new object(); private static XmlSerializer _serializer = null; static Groups() { lock (_syncObject) { if (_serializer == null) { _serializer = new XmlSerializer(typeof(Group[])); } } } private static XmlSerializer Serializer { get { return _serializer; } } public void Serialize(Stream stream) { Group[] groups = new Group[this.Values.Count]; this.Values.CopyTo(groups, 0); using (XmlTextWriter xmlTextWriter = new XmlTextWriter(stream, Encoding.UTF8)) { _serializer.Serialize(xmlTextWriter, groups); } } public static Groups Deserialize(Stream stream) { Group[] groups = _serializer.Deserialize(stream) as Group[]; Groups retGroups = new Groups(); foreach (Group group in groups) { retGroups[group.Name] = group; //group.IsJoined = false; group.ChannelModes = group.ChannelModes == null ? new List() : group.ChannelModes; } return retGroups; } /// /// /// /// /// public static Groups Load(String path) { // group 読み取り if (File.Exists(path)) { TraceLogger.Server.Information(String.Format("Load Group: {0}", path)); try { using (FileStream fs = new FileStream(path, FileMode.Open)) { try { Groups groups = Groups.Deserialize(fs); if (groups != null) return groups; } catch (XmlException xe) { TraceLogger.Server.Information(xe.Message); } catch (InvalidOperationException ioe) { TraceLogger.Server.Information(ioe.Message); } } } catch (IOException ie) { TraceLogger.Server.Information(ie.Message); throw; } } return new Groups(); } /// /// /// /// public void Save(String path) { TraceLogger.Server.Information(String.Format("Save Group: {0}", path)); try { String dir = Path.GetDirectoryName(path); Directory.CreateDirectory(dir); using (FileStream fs = new FileStream(path, FileMode.Create)) { try { this.Serialize(fs); } catch (XmlException xe) { TraceLogger.Server.Information(xe.Message); } catch (InvalidOperationException ioe) { TraceLogger.Server.Information(ioe.Message); } } } catch (IOException ie) { TraceLogger.Server.Information(ie.Message); throw; } } } /// /// IRC上でチャンネルとして表現されるメッセージ送信対象のグループを表します。 /// public class Group : IComparable { /// /// チャンネル名を取得・設定します /// public String Name { get; set; } public String Mode { get; set; } /// /// グループに属するユーザのリストを取得します /// public List Members { get; set; } /// /// JOINしているかどうかを取得・設定します /// public Boolean IsJoined { get; set; } /// /// 特別扱いされるチャンネルかどうかを取得・設定します。 /// 特別扱いされている場合、タイムラインステータスは送信されなくなります。 /// public Boolean IsSpecial { get; set; } /// /// チャンネルのトピックを取得・設定します /// public String Topic { get; set; } /// /// チャンネルのモードを取得・設定します /// public List ChannelModes { get; set; } /// /// グループのインスタンスを初期化します。 /// public Group() { ChannelModes = new List(); } /// /// グループを複製します。 /// /// public Group Clone() { Group g = new Group() { Name = this.Name, ChannelModes = this.ChannelModes, IsJoined = this.IsJoined, IsSpecial = this.IsSpecial, Members = new List(this.Members), Mode = this.Mode, Topic = this.Topic }; return g; } /// /// 指定した名前でグループのインスタンスを初期化します。 /// /// #で始まるチャンネル名 public Group(String name) { if (!name.StartsWith("#") || name.Length < 2) { throw new ArgumentException("チャンネル名は#で始まる必要があります。"); } Name = name; Members = new List(); ChannelModes = new List(); } /// /// 指定したユーザがグループのメンバかどうかを取得します。 /// /// ユーザのID /// グループのメンバに属しているかどうかを表すBoolean値。 public Boolean Exists(String id) { Int32 pos; lock (Members) { pos = Members.BinarySearch(id, StringComparer.InvariantCultureIgnoreCase); } return pos > -1; } /// /// グループのメンバに属するユーザを追加します。 /// /// ユーザのID public void Add(String id) { lock (Members) { Members.Add(id); Members.Sort(StringComparer.InvariantCultureIgnoreCase); } } /// /// 指定したユーザをグループのメンバから削除します。 /// /// ユーザのID public void Remove(String id) { lock (Members) { Members.Remove(id); Members.Sort(StringComparer.InvariantCultureIgnoreCase); } } /// /// グループに送信されたメッセージのエコーバックを無視するかどうかを取得します。 /// public Boolean IgnoreEchoBack { get { return ChannelModes.Exists(mode => mode.Mode == ChannelModeTypes.Private); } } /// /// グループのトピックを利用したマッチがメンバとOR条件になっているかどうかを取得します。 /// public Boolean IsOrMatch { get { return String.IsNullOrEmpty(Topic) ? false : Topic.StartsWith("|"); } } /// /// TwitterIrcGatewayがトピックなどを考慮してメッセージ送信先として選択できるかどうかを取得します。 /// public Boolean IsRoutable { get { return !(IsSpecial || ChannelModes.Exists(mode => mode.Mode == ChannelModeTypes.InviteOnly)); } } public override string ToString() { return String.Format("Group: {0} ({1} members)", Name, Members.Count); } #region IComparable メンバ public int CompareTo(object obj) { if (!(obj is Group)) return -1; return String.Compare((obj as Group).Name, this.Name, true, CultureInfo.InvariantCulture); } #endregion } /// /// 送信先を決定した後を表すクラス /// public class RoutedGroup : IComparable { /// /// 決定対象となったグループを取得します /// public Group Group { get; set; } /// /// 自分自身が送信したメッセージかどうかを取得します /// public Boolean IsMessageFromSelf { get; set; } /// /// チャンネルにユーザが存在しているか、0人のチャンネルだったのかを取得します /// public Boolean IsExistsInChannelOrNoMembers { get; set; } /// /// このチャンネルに送信されるIRCのメッセージの種類を取得または設定します /// public String IRCMessageType { get; set; } /// /// このチャンネルに送信されるテキストを取得または設定します /// public String Text { get; set; } public RoutedGroup() { IRCMessageType = "PRIVMSG"; } #region IComparable メンバ public int CompareTo(object obj) { if (!(obj is RoutedGroup)) return -1; return String.Compare((obj as RoutedGroup).Group.Name, this.Group.Name, true, CultureInfo.InvariantCulture); } #endregion } }