Skype API チャットの送受信 (C#)

C#からSkype APIを使用してチャットの送受信をしてみた。
受信したチャットをもとになんらかの処理をやってみたり、何らかの処理の結果をチャットに送信してみたりと、Skypeチャットとの連携に使えそうである。

Skype4COM


Skype APIを提供するActiveXコンポーネントとのこと。ActiveXコンポーネントを使える環境(VisualStudioとかDelphiとかVisualBasic,PHP,JavaScriptとか)から使えるとのこと。
https://developer.skype.com/accessories/skype4comからSkype4COMをダウンロードし、
解凍するとSkype4COM.dllが入っているので、これをVisualStudioの参照に追加(しておく。

(ソリューションエクスプローラの参照設定の部分で右クリックし、参照の追加を選択、(またはプロジェクト->参照の追加 メニューを選択し)、「参照の追加」ダイアログを表示させ、参照タブからSkype4COM.dllを選ぶ。)

これで using SKYPE4COMLib; が可能となる。

サンプルコード


○チャットの特定 ( getChat() )
アプリからメッセージを送受信したいチャットを特定するために以下をkeyとして探索することにした。
・個人チャットの場合 : 相手のskype名(loftkunとか) 注)表示名じゃない
・グループチャットの場合 : 会話のタイトル(~~会議室とか)
探索の結果Chatオブジェクトが取得できる。
 
○既存メッセージ取り出し ( getMessages() )
・Chatオブジェクトに含まれるメッセージを適当に整形。
 
○メッセージ受信 ( _skype_MessageStatus() )
・MessageStatusイベントハンドラにて受信。

○メッセージ送信 ( chat.SendMessage() )
・ChatオブジェクトのSendMessage()で実施した。(個人チャットは_skype.SendMessage()でもできた。)

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using SKYPE4COMLib;

namespace skypetest
{
    public partial class Form1 : Form
    {
        private SKYPE4COMLib.Skype _skype;
        private string _key;//Chat検索key
        private string BR = Environment.NewLine;//改行文字

        public Form1()
        {
            InitializeComponent();

            //Skypeオブジェクト生成
            _skype = new SKYPE4COMLib.Skype();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //このアプリからメッセージ送受信したいChatオブジェクトを抽出
            //以下を検索keyとしてマッチするChatオブジェクトを抽出する
            //  個人チャットの場合 : 相手のskype名(loftkunとか) 注)表示名じゃない
            //  グループチャットの場合 : 会話のタイトル(~~会議室とか)
            _key = "loftkun";
            SKYPE4COMLib.Chat chat = getChat(_key);
            if(chat == null){
                return;
            }
            
            //既存メッセージ表示
            textBox1.Text = getMessages(chat);

            //メッセージ受信ハンドラ登録
            _skype.MessageStatus += new _ISkypeEvents_MessageStatusEventHandler(_skype_MessageStatus);

            //メッセージ送信
            chat.SendMessage("ほげ~");

            //なお以下のように個人チャットの相手名を指定してメッセージ送信もできるようだ
            //グループチャットの場合、SendMessage()の第一引数が不明なので、
            //上記(chat.SendMessage())を採用している。
            //_skype.SendMessage("loftkun", "ほげほげ"); //loftkun個人宛に送信
        }

        /// <summary>
        /// チャットメッセージ受信イベントハンドラ
        /// </summary>
        /// <param name="pMessage">メッセージ</param>
        /// <param name="Status">送受信Status</param>
        void _skype_MessageStatus(ChatMessage pMessage, TChatMessageStatus Status)
        {
            try
            {
                //このメッセージの属しているチャットがkeyに合致するかチェック
                if (checkKey(_key, pMessage.Chat) == false)
                {
                    return;
                }
                string str = getMessageString(pMessage);

                //表示
                textBox1.Text = str + textBox1.Text;
            }
            catch (Exception)
            {
                textBox1.Text = "Exception" + BR + textBox1.Text;
            }
        }

        /// <summary>
        /// チャットの既存メッセージを取得
        /// </summary>
        private String getMessages(SKYPE4COMLib.Chat chat)
        {
            try
            {
                String str = "";
                foreach (SKYPE4COMLib.ChatMessage msg in chat.Messages)
                {
                    str += getMessageString(msg);//適当に整形
                }
                return str;
            }
            catch (Exception)
            {
                return "";
            }
        }

        /// <summary>
        /// チャットメッセージを表示用に整形
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private string getMessageString(ChatMessage msg)
        {
            //適当に整形
            return msg.Timestamp.ToString()
                + " [" + msg.Status.ToString() + "]"
                + "[" + msg.FromDisplayName + "] "
                + msg.Body + BR;
        }

        /// <summary>
        /// 検索keyにHITするChatオブジェクトを取得する
        /// </summary>
        /// <param name="key">検索key</param>
        /// <returns></returns>
        private SKYPE4COMLib.Chat getChat(String key)
        {
            try
            {
                //foreach (SKYPE4COMLib.Chat chat in _skype.Chats) //全てのチャットっぽい
                foreach (SKYPE4COMLib.Chat chat in _skype.ActiveChats) //アクティブなチャットウィンドウ順っぽい
                {
                    if (checkKey(key, chat) == true)
                    {
                        return chat;
                    }
                }
                return null;
            }
            catch (Exception exp)
            {
                return null;
            }
        }

        /// <summary>
        /// Chatオブジェクトが検索keyにHITするかどうかを判定する
        /// 検索keyは以下
        ///  個人チャットの場合     : 相手のskype名
        ///  グループチャットの場合 : 会話のタイトル
        /// </summary>
        /// <param name="key"></param>
        /// <param name="chat"></param>
        /// <returns></returns>
        private bool checkKey(string key, Chat chat)
        {
            try
            {
                //けっこうあいまいな判定
                switch (chat.Status)
                {
                    case TChatStatus.chsDialog: //個人チャットかな~
                        //個人チャットの相手のskype名
                        if (key == chat.DialogPartner)
                        {
                            return true;
                        }
                        break;
                    case TChatStatus.chsMultiSubscribed://グループチャットかな~
                        //グループチャットの会話のタイトル
                        if (key == chat.Topic)
                        {
                            return true;
                        }
                        break;
                    default://分からん
                        break;
                }
                return false;
            }
            catch (Exception exp)
            {
                return false;
            }
        }
    }
}

15件のコメント

  1. よろしければ、skypeのリファレンスをいただけませんでしょうか、、、
    どこを探してもリンク切れで、、、
    skypeIDは “kokkisan1″ です、お願いします

  2. ロフトくん

    >dossさん
    こんにちは、はじめまして。
    https://developer.skype.com/resources/Skype4COM-1.0.38.0.zip
    でダウンロード出来るようです。dllとヘルプが含まれています。

    昔はサンプルコードなど掲載されていたのですがそのページは見つかりませんでした。
    本ブログのコードはskype バージョン6.6.73.106にて今のところ動作しています。

  3. >ロフトくんさん
    どうもありがとうございました!
    本当に助かりました!
    このブログに書いてあるサンプルコードをもとにskypeでの人工無能などを作成しようと思い、リファレンスがほしかったのですが、これで作成していくことができます。(贅沢をいえば、日本語のが欲しかったですかねwwww)

    厚かましいようですが、よろしければ、特定のチャットをもらった時に決められた処理をするサンプルを書いてはいただけませんでしょうか、、、
    なにぶん、C#は経験が希薄なもので、、、

  4. ロフトくん

    >dossさん
    こんにちは。お返事遅れました。
    特定の文字列が含まれているかはContains()メソッドが便利です。

    /// <summary>
    /// チャットメッセージ受信イベントハンドラ
    /// </summary>
    /// <param name="pMessage">メッセージ</param>
    /// <param name="Status">送受信Status</param>
    void _skype_MessageStatus(ChatMessage pMessage, TChatMessageStatus Status)
    {
        try
        {
            //このメッセージの属しているチャットがkeyに合致するかチェック
            if (checkKey(_key, pMessage.Chat) == false)
            {
                return;
            }
            string str = getMessageString(pMessage);
    
            //サンプル : テキストボックスへの表示
            textBox1.Text = str + textBox1.Text;
    
            //サンプル : ぬるぽバスター
            if (str.Contains("ぬるぽ"))
            {
                 pMessage.Chat.SendMessage("ガッ");
            }
            
            //サンプル : おみくじ
            if (str.Contains("おみくじ"))
            {
                //乱数生成
                //略
                 pMessage.Chat.SendMessage("大吉");//or 中吉 or ,,, 
            }
            
    
        }
        catch (Exception)
        {
            textBox1.Text = "Exception" + BR + textBox1.Text;
        }
    } 
    

    その他、特定の文字列から始まるか(StartsWith())、
    特定の文字列で終わるか(EndsWith())なども便利です。
    参照)MSDN String クラス (System)
    http://msdn.microsoft.com/ja-jp/library/vstudio/system.string.aspx

  5. >ロフトくんさん
    わざわざすいません、ここまで書いてくれるなんて、、、
    これで、かなりいろんなことができるようになりました!
    どうもありがとうございました!!

  6. C#でSkypeのチャットデータを読み書きしたく、このサイトを見つけました。
    SKYPE4COM.DLLへのリンクがあり、それを入手はできたのですが、これって現在でも動作するのでしょうか?
    まずはそのままのコードで動かしてみたところ、

    113 private SKYPE4COMLib.Chat getChat(String key)
    114 {
    115 try
    116 {
    117 //foreach (SKYPE4COMLib.Chat chat in _skype.Chats) //全てのチャットっぽい
    118 foreach (SKYPE4COMLib.Chat chat in _skype.ActiveChats) //アクティブなチャットウィンドウ順っぽい
    119 {
    120 if (checkKey(key, chat) == true)
    121 {
    122 return chat;
    123 }
    124 }
    125 return null;
    126 }
    127 catch (Exception exp)
    128 {
    129 return null;
    130 }
    131 }

    の118行目でエラーになり、Connection refused.という例外が出ます。
    アドバイスいただけるとうれしいです。

    • ロフトくん

      >kangenさん
      初めまして。
      Connection refusedという例外は遭遇したことはないですが、
      最近のSkypeでデスクトップAPIが廃止されていることが理由かもしれません。
      ご参考) https://support.skype.com/ja/faq/FA12349/

      まずはSkypeのバージョンをご確認下さい。
      (私は6.11.0.102を使っています。
      設定でSkypeの自動バージョンアップを行わないようにしています。)

      古いバージョンのSkypeは以下の様なサイトで手に入るかもしれません。
      お試しになる場合は、非公式サイトですので、自己責任にてお試し下さい。
      http://www.oldapps.com/skype.php

  7. 大変有益なアドバイスありがとうございます。
    現在使用しているバージョンは6.16.59.105でした。
    6.11.32.102というのを入手できました。
    これでだめならもうすこしバージョンを落としてみます。
    感謝。

  8. ぴょおおおおおお

    はじめまして、SKypeリファレンスのhttps://developer.skype.com/resources/Skype4COM-1.0.38.0.zip
    このURLが切れているのですが、直接送ってもらえないでしょうか?
    上に書いているメールアドレスに送っていただけたら幸いです。

  9. z変換がさっぱり

    このbotで、特定個人だけではなく、チャットが来た相手に対して固定のメッセージを送るというのはできないのでしょうか?少しばかりいじっては見たもののやはり、個人かグループに対してのしかできなかったもので。

    • ロフトくん

      >>z変換がさっぱりさん
      こんにちは、はじめまして。

      こんな感じでいけそうです。

      private void _skype_MessageStatus(ChatMessage pMessage, TChatMessageStatus Status)
      {
          try
          {
              //メッセージ送信者のハンドル名取得
              String name = pMessage.FromHandle;
      
      
              //送信者が自分でなければ、何か返信する
              if (name != "loftkun")
              {
                  _skype.SendMessage(name, "テスト");
              }
          }
          catch (Exception exp)
          {
          }
      }
      
  10. z変換がさっぱり

    >>ロフトくんさん
    ありがとうございます!!目的が達成できました!!
    達成できたのですが、今度は小さな不具合が生まれてしまいます。
    下記のようなソースを書きました。
    void _skype_MessageStatus(ChatMessage pMessage, TChatMessageStatus Status)
    {
    try
    {
    //メッセージ送信者のハンドル名取得
    String name = pMessage.FromHandle;
    string str = getMessageString(pMessage);
    bool check = true;

    //送信者が自分でなければ、何か返信する
    if (name != “kokkis7″){
    //ファイルの読み込み
    System.IO.StreamReader sr = new StreamReader(@”Skype_Chat.txt”);

    while (!sr.EndOfStream)
    if (str.Contains(sr.ReadLine()))
    {
    System.IO.StreamReader reply = new StreamReader(@”Skype_Chat.txt”);
    string[] linenum = File.ReadAllLines(@”Skype_Chat.txt”);
    Random rnd = new Random();
    for (int i = 0; i < rnd.Next(linenum.Length); i++)
    reply.ReadLine();
    _skype.SendMessage(name, reply.ReadLine());
    reply.Close();
    break;
    }
    sr.Close();

    sr = new StreamReader(@"Skype_Chat.txt");
    //ファイルの終わりまで読み込む
    while (!sr.EndOfStream)
    //発言がファイルの中にあるか
    if (str == sr.ReadLine()) {
    check = false;
    }
    sr.Close();

    //発言がファイルの中になければ
    if (check == true){
    //登録する
    System.IO.StreamWriter f = new StreamWriter(@"Skype_Chat.txt", true);
    f.Write(str);
    f.Close();
    }
    }
    }
    catch (Exception){}
    }
    上記のソースはテキストフォルダに発言を追加していき、条件が一致した場合にランダムに蓄積されてきた発言を返す、といったものなのですが、二回連続で変身してしまうときや、同じ発言を二つ登録してしまう、などの不具合が起きます。
    これは何が原因なのでしょうか?

    長々と申し訳ありません。

    • ロフトくん

      >z変換がさっぱりさん
      こんにちは。
      いくつか気づいた点を記載します。
      ・ReadAllLines()すると全行の文字がlinenumに入るので、
      ある行の文字列はlinenum[0始まりの行番号]で取得できます。
       これを利用するとreply.ReadLine()を省くことが出来ます。

      ・rnd.Next()は実行する度に新しい乱数が返ります。
      for文はループする度にfor文の終了条件を確認するので、
      その度に新しい乱数を作って終了判定してしまっています。

      上記の点を反映してみました(目的にあっていますでしょうか?)
      若干処理順序や変数名など元のソースコードから変えている箇所がありますのでお気をつけ下さい。

      void _skype_MessageStatus(ChatMessage pMessage, TChatMessageStatus Status)
      {
          try
          {
              //メッセージ送信者のハンドル名取得
              String name = pMessage.FromHandle;
              //自分の発言ならば終了
              if (name == "kokkis7")
              {
                  return;
              }
      
              //発言取得
              string str = pMessage.Body;
      
              //ファイルの読み込み
              //全行読み込む
              string[] allLines = File.ReadAllLines(@"Skype_Chat.txt");
      
              //行数取得
              int allLineNum = allLines.Length;
      
              //先頭行から順番に処理
              bool check = true;
              for (int i = 0; i < allLineNum; i++)
              {
                  //現在行の文字列
                  String line = allLines[i];
      
                  //現在行の文字列が発言に含まれるか
                  if (!str.Contains(line))
                  {
                      //含まれない
                      continue;//次の行へ
                  }
      
                  //含まれる
                  check = false;
      
                  //ランダムな行番号(0始まり)を決定
                  int randomLineNo = new Random().Next(allLineNum);
      
                  //ランダムな行番号の文字列を取得
                  String randomLineStr = allLines[randomLineNo];
      
                  //送信
                  _skype.SendMessage(name, randomLineStr);
      
                  break;
              }
      
              //発言がファイルの中にあるか
              if (check == true)
              {
                  //登録する
                  System.IO.StreamWriter f = new StreamWriter(@"Skype_Chat.txt", true);
                  f.Write(str);
                  f.Close();
              }
          }
          catch (Exception)
          {
      
          }
      }
      
      • z変換がさっぱり

        >>ロフトくんさん
        ありがとうございました!!
        自分の作りたかったものが完成しました!!
        お世話になりました!!

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です