habrahabr

IT чаты или Выжимаем из скайпа все соки

  • понедельник, 10 ноября 2014 г. в 02:10:49
http://habrahabr.ru/post/242683/

image Часто у новичков в той или иной IT области ощущается острый дефицит знаний и знакомых, у которых можно что-либо «спросить» по теме. Да, StackOverflow, Google и другие подобные ресурсы — просто кладезь полезной информации, однако, согласитесь, бывают и ситуации, когда вопрос настолько общий, что ответ на него может дать только опытный человек работающий в этой сфере не первый год.

По большей части это касается этих самых новичков, потому что матёрые айтишники мало того что могут из гугла и душу чужую вытащить, так ещё обычно и знакомых у них в этой сфере хватает — вопрос так или иначе решится.

И вот тут пролетела мысль — а почему бы не сделать IT-чаты и разбить их на определённые темы/технологии? Ок, попробуем. И Skype нам тут поможет.

Если Вы хотите узнать почему был выбран скайп, каким образом можно «обойти» лимит в 300 человек на один чат или у Вас просто спряталась альтруистичная IT нотка и вы любите помогать коллегам — берите печеньки и добро пожаловать под кат.










Почему Skype?


Существует множество онлайн бордов/форумов, где вы можете пойти и задать свой специфичный вопрос. Однако есть несколько НО:

1) Это там. А скайп тут. И между этими «там» и «тут» на мой взгляд — пропасть. Попросить кого-то сделать что-то, куда-то там зайти, где-то там зарегистрироваться — нереальный кейс. Как говорят технари и продаваны — конверсия будет нулевой. Серьёзно, люди ленивы, особенно когда не мотивированы, поэтому пытаемся максимально упростить «вход» и пробуем использовать то, что есть под рукой у каждого.

2) Пинг. Вы зашли, написали и… ждёте. Выяснение вопроса может затянутся на дни. Поэтому и не пишем, а если и пишем, то только уже по совсем завальным ситуациям, где ни знакомые, ни гугл не смогли нам помочь, но обычно таких вопросов у каждого отдельного человека возникает не так уж и много, верно?

Есть конечно же и минусы подобного решения:

1) Скайп. Он не так хорош, насколько хотелось бы, однако предоставляет базовый функционал для ведения групп, что в общем-то уже неплохо.
2) Отсутствие истории сообщений. Нет, вы можете конечно проскроллить до энного момента, но все мы понимаем, что это не тот поиск, который требуется.
3) Возможность спамить. Разговор между двумя людьми будет затрагивать всех. /alertsoff будет тут лучшим другом и товарищем.

На мой взгляд, плюсы перевешивают, потому что когда есть цель — все преграды это лишь преграды, не так ли? Было бы желание, и азбукой Морзе можно перестукиваться с соседями :)

Как уместить всех желающих?



Скайп зачем-то сделал ограничение в 300 участников на одну группу. Зачем в общем-то понятно, но нас это не устраивает. Что, если в определённые чаты захочет зайти больше трёхсот человек? Я нашёл выход в синхронизации нескольких чатов между собой посредством Чат бота, написанного в свободное время. Он нам и поможет, связав нужные чаты между собой путём трансляции сообщений из одной группы в другую и наоборот. Костыль? Костыль. Но что поделать.

Как эта синхронизация работает. Куски реализации и объяснение механизма связывания. C#
Боту нужно к чему-то привязаться. Топик группы не являлся лучшим решением и я обнаружил, что при попытке взять Name чата, api отдавала и его id.

Этот id есть у каждого чата и он уникален. Получить его можно только через api. Чтобы его узнать — нужно добавить бота (skypename: «mensclubbot» или же вашу личная запущенная реализация/форк) в оба чата и выполнить в каждом из них команду "!get id", после чего бот выдаст уникальный id чата.

Дальше идём в app.config и настраиваем синхронизацию подобным образом:



Формат:

— {id}: id чата
— {group name} — название группы, которое будут видеть члены противоположного чата, можно оставить пустым, в таком случае чат указываться при трансляции указываться не будет.
— {from}: направление трансляции сообщений. Может быть «from», «to», «both».

В итоге у вас должно получится что-то подобное:

image

Добавлять новые конфигурации можно через ";".

Всё готово, теперь сообщения из одного чата транслируются в другой и наоборот. Выглядит это как-то так:

image

C# код класса синхронизации
public class SkypeChatSyncer
    {
        private List<ChatSyncerRelation> chatSyncRelations { get; set; }

        public delegate void OnSendMessageRequiredDelegate(string message, string toSkypeId);
        public event OnSendMessageRequiredDelegate OnSendMessageRequired;

        public SkypeChatSyncer()
        {
            chatSyncRelations = new List<ChatSyncerRelation>();
            LoadRelations();
        }

        private void LoadRelations()
        {
            string configValue = ConfigurationManager.AppSettings["ChatSyncRelations"];
            if (!string.IsNullOrEmpty(configValue))
            {
                var relations = configValue.Split(';');
                foreach (string relation in relations)
                {
                    var relationParts = relation.Split('|');
                    if (relationParts.Length == 3)
                    {
                         
                        var fromChatIdParts = relationParts[0];
                        var toChatIdParts = relationParts[2];
                        
                        string relationOperator = relationParts[1];
                        if (relationOperator == "from")
                        {
                            string tempChatId = fromChatIdParts;
                            fromChatIdParts = toChatIdParts;
                            toChatIdParts = tempChatId;
                        }
                        chatSyncRelations.Add(new ChatSyncerRelation(fromChatIdParts, toChatIdParts));

                        if (relationOperator == "both")
                        {
                            //reverse
                            chatSyncRelations.Add(new ChatSyncerRelation(toChatIdParts,fromChatIdParts));
                        }
                    }
                }
            }
        }

        public void HandleMessage(string message, string fromName, string fromChatId)
        {
            var sendToChats = chatSyncRelations.Where(x => x.FromChatId == fromChatId).ToList();
            foreach (var chat in sendToChats)
            {
                string formattedMessage = string.Format("[{0}]{1} : {2}", chat.FromChatName, fromName, message);
                if (OnSendMessageRequired != null)
                {
                    OnSendMessageRequired(formattedMessage, chat.TochatId);
                }
            }
            
        }
    }



Пользуем вот так
class Program
    {
        private static Skype skype = new Skype();
        private static HelloBot bot;
        private static SkypeChatSyncer chatSyncer;
        private static IDictionary<string,IChat> chats { get; set; }
        private static object _chatLocker = new object();

        static void Main(string[] args)
        {
            bot = new HelloBot();
            bot.OnErrorOccured += BotOnErrorOccured;
            Task.Run(delegate
            {
                try
                {
                    skype.MessageStatus += OnMessageReceived;
                    skype.Attach(5, true);
                    chatSyncer = new SkypeChatSyncer(); //init chat sync
                    chatSyncer.OnSendMessageRequired += ChatSyncerOnOnSendMessageRequired;
                    Console.WriteLine("skype attached");
                }
                catch (Exception ex)
                {
                    Console.WriteLine("top lvl exception : " + ex.ToString());
                }
                while (true)
                {
                    Thread.Sleep(1000);
                }
            });
            
            while (true)
            {
                Thread.Sleep(1000);
            }
        }

        private static void ChatSyncerOnOnSendMessageRequired(string message, string toSkypeId)
        {
            var chat = GetChatById(toSkypeId);
            if (chat != null)
            {
                SendMessage(message,chat);
            }
        }

        private static IChat GetChatById(string chatId)
        {
            if (chats == null)
            {
                lock (_chatLocker)
                {
                    if (chats == null)
                    {
                        chats = new Dictionary<string, IChat>();
                        foreach (IChat chat in skype.Chats)
                        {
                            string tChatId = chat.Name.Split(';').Last();
                            chats.Add(tChatId,chat);
                        }
                    }
                }
            }

            IChat toReturn = null;
            chats.TryGetValue(chatId, out toReturn);

            return toReturn;
        }

        static void BotOnErrorOccured(Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }

        private static void OnMessageReceived(ChatMessage pMessage, TChatMessageStatus status)
        {
            Console.WriteLine(status + pMessage.Body);
 
            if (status == TChatMessageStatus.cmsReceived)
            {
                bot.HandleMessage(pMessage.Body, answer => SendMessage(answer,pMessage.Chat),new SkypeData(pMessage));

                string fromChatId = pMessage.Chat.Name.Split(';').Last();
                chatSyncer.HandleMessage(pMessage.Body,pMessage.FromDisplayName,fromChatId);
            }
        }
        

        public static object _lock = new object();
        private static void SendMessage(string message, IChat toChat)
        {
            if (message.StartsWith("/"))
            {
                message = "(heidy) " + message;
            }
                lock (_lock)
                {
                    toChat.SendMessage(message);
                }
        }
    }





Ну окей, как мне посмотреть список чатов и вступить в один из них?



Посмотреть список чатов можно тут :
docs.google.com/spreadsheets/d/1re0ntO6ZpPprYrMpKKuV_7I367Th30iRZWEL6ThXkUg — это список чатов с кратким описанием. Их мало, но если нужны ещё какие-то, то пишите, обязательно добавлю.

image

Непосредственно вступить можно пройдя по этой ссылочке: jsfiddle.net/KTt3V/1/ (она будет менятся при добавлении новых чатов, поэтому возьмите её лучше лучше из ссылки выше, на гуглдоксах) и кликнув интересующую группу. Браузер подхватит скайп протокол и скайп присоединит вас к группе.

Правила?


Я понимаю, что алерты в скайпе появляются даже если их выключить через /alertsoff, поэтому если вам вдруг захотелось задать вопрос или ответить на него, или просто пообщаться — уважайте других участников чата, старайтесь писать только по делу :)

Заключение


Я прекрасно понимаю, что это решение через "Завод по производству кирпичей Бендера", однако, возможно, кому-то всё же эти группы будут полезны.

Ссылки


Выбрать чат по душе и вступить можно тут: docs.google.com/spreadsheets/d/1re0ntO6ZpPprYrMpKKuV_7I367Th30iRZWEL6ThXkUg
Гитхаб чат бота: github.com/Nigrimmist/HelloBot
Описания команд скайпа: habrahabr.ru/post/97561/

Спасибо за внимание и… Присоединяйтесь! — потому что сила именно в сообществах.
Об ошибках в тексте сообщайте пожалуйста в личку. Спасибо!