前回、ニコニコ生放送用ツールとして
・アラート機能
・コメビュ機能
・1コメ投稿機能
をもつものをC#で作ってみました(とりあえず ニコ生アラート(苺) CUI版と名づけてます)。
問題点として、このツールがログインすると他のツール(WEBブラウザ)がログアウトしてしまいます。
これを防ぐために、今回はWEBブラウザとクッキーを共有させてみました。
これで、ブラウザがログアウトしなくなります。
また、このツールへのメアド、パスワードの入力も不要となります。
サンプルということで今回もCUIです。
2011/12/19 ソースコードの文字列中のエスケープ文字が消えていたので修正しました。
\n → n 、 \” → ” になっていました。
2011/12/26 うつろ氏のCookieGetterSharp.dllをメインに書きなおしました。
WEBブラウザからクッキー取得
CookieGetterSharp.dllを利用
クッキー取得ライブラリである、CookieGetterSharp.dllを使わせて頂きました。
もともと、ニコ生用コメントビューア「nwhois」の作者であるhalxxxxさんが作成(改造&公開)していましたが、
更新が止まっているため、Firefox4以降等、最近のブラウザに対応していません。
ニコニコミュニティ悠悠閑閑前途遼遠のコミュ主である、うつろ氏が最新のブラウザへの対応を実施されていますので、
これから導入される方はそちらをお勧めします。
ダウンロード方法はコミュニティ悠悠閑閑前途遼遠を参照して下さい。
(2011/12/19現在の最新版は、CookieGetterSharpDistribution 20111207です。)
VisualStudioでの開発の場合、プロジェクトの参照設定->参照の追加、からCookieGetterSharp.dllを追加して使用します。
クッキー取得処理
Firefox4以降の場合の例です。
他のブラウザのクッキーを使用するには、CreateInstance()引数に指定するBrowserType値を変更します。
ICookieGetter cookieGetter = CookieGetter.CreateInstance(BrowserType.Firefox4);
CookieCollection collection = cookieGetter.GetCookieCollection(new Uri("http://live.nicovideo.jp/"));
Cookie user_session = collection["user_session"];
m_cc = new CookieContainer();
m_cc.Add(user_session);
BrowserType値の一覧は、CookieGetterSharp.dllのバージョンによって変わります。
VisualStudioのエディタで”BrowserType”と記述し、マウスで選択、右クリック->定義へ移動 で確認できます、以下は例です。
namespace Hal.CookieGetterSharp
{
public enum BrowserType
{
IE = 0,
IEComponent = 1,
IESafemode = 2,
Firefox3 = 3,
Firefox3Portable = 4,
Firefox4 = 5,
GoogleChrome3 = 6,
GoogleChrome3Portable = 7,
ChromePlus = 8,
Opera1x = 9,
Opera11beta = 10,
Safari4 = 11,
Lunascape5Gecko = 12,
Lunascape6Gecko = 13,
Lunascape6Webkit = 14,
Chromium = 15,
}
}
クッキー設定処理
上記で取得済みのクッキーコンテナをHTTP GETリクエスト時に使用します。
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); req.CookieContainer = m_cc;//取得済みのクッキーコンテナ
サンプルツール使用法
そのままビルドすると、以下指定となります。
このツール.exe [コミュID] [生主ID]
コミュID、生主IDは、アラートでひっかけたい放送のIDです、
指定なし、片方のみ指定、両方指定できます。
指定なしの場合は、最初にみつけた放送に問答無用で
1コメ投稿しますので、取扱注意です。
起動例)
このツール.exe
起動後、最初に開始した放送に接続し、1コメを送信。
このツール.exe co247262
コミュニティID=co247262の放送が開始したら接続し、1コメを送信。
このツール.exe 426136
生主ID=426136の放送が開始したら接続し、1コメを送信。
このツール.exe co247262 426136
コミュニティID=co247262、または生主ID=426136の放送が開始したら接続し、1コメを送信。
ソースコード
自由に改造して頂いて構いません。
コミュ限放送判定、disconnect検出処理などはないので注意してください。
完全に俺ツールなので心して改造してください。
using System;
using System.Text;
using System.Xml;
using System.Net;
using System.IO;
using System.Diagnostics;
using System.Collections;
using Hal.CookieGetterSharp;
namespace NicoLive_Alert_ComenntView_1CommentSend
{
class Program
{
//ログイン情報
static string m_mail = "";//メアド(ブラウザとクッキー共有のため不要)
static string m_pass = "";//パスワード(ブラウザとクッキー共有のため不要)
static CookieContainer m_cc;//クッキーコンテナ
//アラート設定
static long m_AlertCount = 0; //アラート回数
static long m_MaxAlertCount = 1;//最大アラート回数
//アラートで引っかけたいID(//コマンドライン第3, 4引数)
static string m_comID;//コミュID
static string m_usrID;//生主ID
//ログインAPI戻り値
static string m_ticket = "";//認証用チケット
//認証API(getalertstatus)戻り値
static string m_addr = "";//番組開始情報サーバのアドレス
static string m_port = "";//番組開始情報サーバのポート
static string m_thread = "";//スレッドID
//番組詳細情報取得API(getplayerstatus)戻り値
//番組情報
static string m_base_time = "";//開始時刻
//ユーザ情報
static string m_userid = "";//ユーザID
//コメントサーバ情報
static string m_ComSrvAddr = "";//コメントサーバのアドレス
static string m_ComSrvPort = "";//コメントサーバのポート
static string m_ComSrvThread = "";//スレッドID
//getpostkey戻り値
static string m_postkey = "";
//他管理情報
static DateTime m_DateTimeStart;//開始時刻(ローカル)
static void Main(string[] args)
{
//コマンドライン引数チェック
if (!CheckCommandLine(args))
{
return;
}
//クッキーの取得
{
//クッキーコンテナ取得のため、普通にログインする
//このツールがログインすると、
//他のツール(ブラウザ等)がログアウトするので
//ぼつとなった。(GetCookieFromebBrowserを使用)
//if (!LoginForCokkieContainer())
//{
// return;
//}
//ログイン済みのWEBブラウザからクッキーを取得する
if (!GetCookieFromebBrowser())
{
return;
}
}
//認証APIを叩く
{
//以下要認証のAPIは叩く必要がなくなった。
//ログインAPIを叩く(認証API(要ログイン)叩く用)
//if (!LoginForGetAlertStatus())
//{
// return;
//}
//認証API(要ログイン)を叩く
//if (!GetAlertStatus())
//{
// return;
//}
//認証API(ログイン不要)を叩く。
if (!GetAlertInfo())
{
return;
}
}
//番組情報取得、アラート
if (!GetProgramInfo())
{
return;
}
}
//コマンドライン引数チェック
static bool CheckCommandLine(string[] args)
{
try
{
//if (args.Length < 2)
//{
// Console.WriteLine("使用法 : NicoLiveOneComementGetter.exe mail pass [comID] [userID]");
// return false;
//}
//ログイン設定
//m_mail = args[0];
//m_pass = args[1];
//アラートターゲット設定
switch (args.Length)
{
case 0://引数なし(検出次第ターゲットとする)
m_comID = "";
m_usrID = "";
break;
case 1://引数1つ(コミュID, 生主IDどちらかでアラート)
if (args[0].StartsWith("co"))
{
m_comID = args[0];
}
else
{
m_usrID = args[0];
}
break;
case 2://引数2つ(コミュID, 生主ID両方でアラート)
default://それ以上(3つめ以降無視)
m_comID = args[0];
m_usrID = args[1];
break;
}
}
catch (Exception e)
{
Console.WriteLine("Message :n" + e.Message);
Console.WriteLine("Type :n" + e.GetType().FullName);
Console.WriteLine("StackTrace :n" + e.StackTrace.ToString());
return false;
}
return true;
}
//クッキーコンテナ取得のため、このツールで普通にログインする
//この場合、ブラウザなど、他のツールのログインは解除される。
static bool LoginForCokkieContainer()
{
try
{
string url = "https://secure.nicovideo.jp/secure/login?site=nicolive";
//ログイン・ページへのアクセスパラメタ
Hashtable vals = new Hashtable();
//vals["next_url"] = "recent?tab=common";
vals["next_url"] = "";
vals["mail"] = m_mail;
vals["password"] = m_pass;
//パラメタを"param1=value1¶m2=value2"の形にまとめる
string param = "";
foreach (string k in vals.Keys)
{
param += String.Format("{0}={1}&", k, vals[k]);
}
byte[] data = Encoding.ASCII.GetBytes(param);
//HTTP POSTリクエストの作成
m_cc = new CookieContainer(); //認証用クッキーを格納するコンテナ
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = data.Length;
req.CookieContainer = m_cc;
//POSTを実行
Stream reqStream = req.GetRequestStream();
reqStream.Write(data, 0, data.Length);
reqStream.Close();
//HTTP GETによるクッキーの取得
//GET実行
WebResponse res = req.GetResponse();
Stream resStream = res.GetResponseStream();
Encoding encoder = Encoding.GetEncoding("UTF-8");
StreamReader sr = new StreamReader(resStream, encoder);
string xml = sr.ReadToEnd();
sr.Close();
resStream.Close();
//Console.WriteLine("○ログインAPIレスポンス取得");
//Console.WriteLine(xml);//表示
//Console.WriteLine();
//XMLファイル保存サンプル
//StreamWriter sw = new StreamWriter(@"C:hogeNicoLiveOneCommentGetter.log");
//sw.Write(xml);
//sw.Close();
}
catch (Exception e)
{
Console.WriteLine("Message :n" + e.Message);
Console.WriteLine("Type :n" + e.GetType().FullName);
Console.WriteLine("StackTrace :n" + e.StackTrace.ToString());
return false;
}
return true;
}
//ログイン済みブラウザからクッキーを取得する
static bool GetCookieFromebBrowser()
{
try
{
ICookieGetter cookieGetter = CookieGetter.CreateInstance(BrowserType.Firefox4);
CookieCollection collection = cookieGetter.GetCookieCollection(new Uri("http://live.nicovideo.jp/"));
Cookie user_session = collection["user_session"];
m_cc = new CookieContainer();
m_cc.Add(user_session);
}
catch (Exception e)
{
Console.WriteLine("Message :n" + e.Message);
Console.WriteLine("Type :n" + e.GetType().FullName);
Console.WriteLine("StackTrace :n" + e.StackTrace.ToString());
return false;
}
return true;
}
//ログインAPIを叩く(認証API(要ログイン)叩く用)
static bool LoginForGetAlertStatus()
{
try
{
string url = "https://secure.nicovideo.jp/secure/login?site=nicolive_antenna";
//ログイン・ページへのアクセスパラメタ
Hashtable vals = new Hashtable();
vals["mail"] = m_mail;
vals["password"] = m_pass;
//パラメタを"param1=value1¶m2=value2"の形にまとめる
string param = "";
foreach (string k in vals.Keys)
{
param += String.Format("{0}={1}&", k, vals[k]);
}
byte[] data = Encoding.ASCII.GetBytes(param);
//HTTP POSTリクエストの作成
//m_cc = new CookieContainer(); //認証用クッキーを格納するコンテナ
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = data.Length;
//req.CookieContainer = m_cc;
//POSTを実行
Stream reqStream = req.GetRequestStream();
reqStream.Write(data, 0, data.Length);
reqStream.Close();
//HTTP GETによるクッキーの取得
//GET実行
WebResponse res = req.GetResponse();
Stream resStream = res.GetResponseStream();
Encoding encoder = Encoding.GetEncoding("UTF-8");
StreamReader sr = new StreamReader(resStream, encoder);
string xml = sr.ReadToEnd();
sr.Close();
resStream.Close();
Console.WriteLine("○ログインAPIレスポンス取得");
Console.WriteLine(xml);//表示
Console.WriteLine();
//上記の例として、以下のようなXMLが返ってくる、
//<ticket>チケット</ticket>を取得する
//<?xml version="1.0" encoding="utf-8"?>
//<nicovideo_user_response status="ok">
//<ticket>nicolive_antenna_42613614798762701880494232</ticket>
//</nicovideo_user_response>
//XML解析
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(xml);
XmlElement root = xdoc.DocumentElement;
string status = root.Attributes[0].Value;
if (status != "ok")
{
Console.WriteLine("×ログインに失敗しました");
return false;
}
XmlNodeList tickets = root.GetElementsByTagName("ticket");
m_ticket = tickets[0].InnerText;
//表示
Console.WriteLine("○ログイン情報");
Console.WriteLine(" ログイン " + status);
Console.WriteLine(" チケット " + m_ticket);
Console.WriteLine();
}
catch (Exception e)
{
Console.WriteLine("Message :n" + e.Message);
Console.WriteLine("Type :n" + e.GetType().FullName);
Console.WriteLine("StackTrace :n" + e.StackTrace.ToString());
return false;
}
return true;
}
//認証API
static bool GetAlertStatus()
{
try
{
string url = "http://live.nicovideo.jp/api/getalertstatus?ticket=" + m_ticket;
//XML取得
System.Net.WebClient wc = new System.Net.WebClient();
wc.Encoding = System.Text.Encoding.UTF8;
string xml = wc.DownloadString(url);
wc.Dispose();
Console.WriteLine("○認証API(getalertstatus)レスポンス取得");
Console.WriteLine(xml);//表示
//上記の例として、以下のようなXMLが返ってくる
//サーバ情報は<addr>, <port>, <thread>から取得する
//参加コミュも判明する
//<getalertstatus status="ok" time="1293425828">
//<user_id>426136</user_id>
//<user_hash>A_bCdEfGhiJkLmN_OpQ</user_hash>
//<user_name>ロフトくん</user_name>
//<user_prefecture>99</user_prefecture>
//<user_age>3</user_age>
//<user_sex>1</user_sex>
//<is_premium>1</is_premium>
//
//<communities>
//<community_id>co247262</community_id>
//<community_id>co*****</community_id>
//<community_id>co*****</community_id>
//<community_id>co*****</community_id>
//<community_id>co*****</community_id>
//<community_id>co*****</community_id>
//<community_id>co******</community_id>
//<community_id>co*****</community_id>
//</communities>
//
//<ms>
//<addr>twr01.live.nicovideo.jp</addr>
//<port>2533</port>
//<thread>1000000016</thread>
//</ms>
//</getalertstatus>
//XML解析
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(xml);
XmlElement root = xdoc.DocumentElement;
XmlNodeList ms = root.GetElementsByTagName("ms");
//余談だが、ms.Countは常に0と想定し、ms[0]のみ読む。
m_addr = ms[0].ChildNodes[0].InnerText;
m_port = ms[0].ChildNodes[1].InnerText;
m_thread = ms[0].ChildNodes[2].InnerText;
//表示
Console.WriteLine("○番組開始情報サーバ情報");
Console.WriteLine(" アドレス " + m_addr);
Console.WriteLine(" ポート " + m_port);
Console.WriteLine(" スレッドID " + m_thread);
Console.WriteLine();
}
catch (Exception e)
{
Console.WriteLine("Message :n" + e.Message);
Console.WriteLine("Type :n" + e.GetType().FullName);
Console.WriteLine("StackTrace :n" + e.StackTrace.ToString());
return false;
}
return true;
}
//認証API(getalertinfo)を叩く
static bool GetAlertInfo()
{
try
{
string url = "http://live.nicovideo.jp/api/getalertinfo";
//XML取得
System.Net.WebClient wc = new System.Net.WebClient();
wc.Encoding = System.Text.Encoding.UTF8;
string xml = wc.DownloadString(url);
wc.Dispose();
Console.WriteLine("○認証API(getalertinfo)レスポンス取得");
Console.WriteLine(xml);//表示
//上記の例として、以下のようなXMLが返ってくる、
//サーバ情報は<addr>, <port>, <thread>から取得する
//<getalertstatus status="ok" time="1293371009">
//<user_id>Anonymous</user_id>
//<user_hash>-CS4mPdPnMXL8cQJ2g4QakgwvuQ</user_hash>
// <ms>
// <addr>twr02.live.nicovideo.jp</addr>
// <port>2533</port>
// <thread>1000000017</thread>
// </ms>
//</getalertstatus>
//XML解析
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(xml);
XmlElement root = xdoc.DocumentElement;
XmlNodeList ms = root.GetElementsByTagName("ms");
//余談だが、ms.Countは常に0と想定し、ms[0]のみ読む。
m_addr = ms[0].ChildNodes[0].InnerText;
m_port = ms[0].ChildNodes[1].InnerText;
m_thread = ms[0].ChildNodes[2].InnerText;
//表示
Console.WriteLine("○サーバ情報");
Console.WriteLine(" アドレス " + m_addr);
Console.WriteLine(" ポート " + m_port);
Console.WriteLine(" スレッドID " + m_thread);
Console.WriteLine();
}
catch (Exception e)
{
Console.WriteLine("Message :n" + e.Message);
Console.WriteLine("Type :n" + e.GetType().FullName);
Console.WriteLine("StackTrace :n" + e.StackTrace.ToString());
return false;
}
return true;
}
//番組情報取得
static bool GetProgramInfo()
{
try
{
//ホスト名からIPアドレスを取得
IPAddress hostadd = Dns.GetHostEntry(m_addr).AddressList[0];
//IPEndPointを取得
IPEndPoint ephost = new IPEndPoint(hostadd, int.Parse(m_port));
//Socketの作成
System.Net.Sockets.Socket sock = new System.Net.Sockets.Socket(
System.Net.Sockets.AddressFamily.InterNetwork,
System.Net.Sockets.SocketType.Stream,
System.Net.Sockets.ProtocolType.Tcp);
//接続
sock.Connect(ephost);
//リクエストメッセージを送信
//注)最後に'\0'を挿入しないとレスポンスは返ってこない
string param = String.Format("<thread thread=\"{0}\" version=\"20061206\" res_from=\"-1\"/>\0", m_thread);
byte[] data = Encoding.UTF8.GetBytes(param);
sock.Send(data, data.Length, System.Net.Sockets.SocketFlags.None);
//受信する
const int MAX_RECEIVE_SIZE = 1024 * 100;
string prev = "";
while (m_AlertCount < m_MaxAlertCount)
{
byte[] resBytes = new byte[MAX_RECEIVE_SIZE];
int resSize = sock.Receive(resBytes, resBytes.Length, System.Net.Sockets.SocketFlags.None);
if (resSize == 0)
{
break;
}
string xml = prev + Encoding.UTF8.GetString(resBytes, 0, resSize);
Console.WriteLine("○データ受信(" + resSize.ToString() + "byte)");
//Console.WriteLine(xml);//全受信XML表示
//XML解析
//<thread hoge />\0<chat>情報</chat>\0<chat>情報</chat>\0の形で受信する
//<thread hoge />は捨て、<chat>情報</chat>から情報を取り出す
xml = xml.Replace('\0', '\n');
string[] lines = xml.Split('\n');
foreach (string line in lines)
{
Console.WriteLine(line);//受信XML表示(1行)
if (line != "" && !line.EndsWith(">"))
{
//MAX_RECEIVE_SIZEいっぱいに受信した場合等
//XMLが閉じていない場合は次回Receive時結合する
prev = line;
break;
}
if (line.StartsWith("<chat"))
{
//<chat>ここ</chat>を取り出す
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(line);
string[] infos = xdoc.InnerText.Split(',');
if (infos.Length != 3)
{
//番組通知が1分間行われなかったときに発行されるダミーデータ
//chatタグのテキスト部分に現在のUnixタイムが格納されるらしい
continue;
}
Console.WriteLine(" 放送 : " + infos[0]);
Console.WriteLine(" コミュ : " + infos[1]);
Console.WriteLine(" 生主 : " + infos[2]);
//アラート判定
if (((infos[1] == m_comID) || (infos[2] == m_usrID))
|| ((m_comID == "") && (m_usrID == "")))
{
//引っかかったら(または最初の放送をターゲットとする場合)ブラウザで開く
Process.Start("http://live.nicovideo.jp/watch/lv" + infos[0]);
//番組情報を取得
if (!GetPlayerStatus(infos[0]))
{
break;
}
else
{
//コメントサーバ接続
GetCommnet();
}
m_AlertCount++;
}
}
if (m_AlertCount >= m_MaxAlertCount)
{
break;
}
}
}
//閉じる
sock.Shutdown(System.Net.Sockets.SocketShutdown.Both);
sock.Close();
}
catch (Exception e)
{
Console.WriteLine("Message :n" + e.Message);
Console.WriteLine("Type :n" + e.GetType().FullName);
Console.WriteLine("StackTrace :n" + e.StackTrace.ToString());
return false;
}
return true;
}
static bool GetPlayerStatus(string liveID)
{
try
{
string url = "http://live.nicovideo.jp/api/getplayerstatus?v=lv" + liveID;
//string url = "http://live.nicovideo.jp/watch/lv" + liveID;
#if ぼつ
//以下のようにただのDownloadStringでは
//未ログインと怒られるのでぼつとした
//XML取得
System.Net.WebClient wc = new System.Net.WebClient();
wc.Encoding = System.Text.Encoding.UTF8;
string xml = wc.DownloadString(url);
wc.Dispose();
#else
//HTTP GET リクエストの作成
//注)本APIから情報取得するには、ログインが必要っぽい
// しかし認証用API(要ログイン)はクッキーをくれなかった。
// そこで普通のログイン関数にてクッキーコンテナを取得、ここで使用している
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.CookieContainer = m_cc;//取得済みのクッキーコンテナ
//req.CookieContainer.SetCookies(new System.Uri("http://live.nicovideo.jp/"), m_UserSession.Name + ";" + m_UserSession.Value);
WebResponse res = req.GetResponse();
Stream resStream = res.GetResponseStream();
StreamReader sr = new StreamReader(resStream, Encoding.UTF8);
string xml = sr.ReadToEnd();
sr.Close();
resStream.Close();
#endif
//上記の例として、以下のようなXMLが返ってくる。
//stream : 番組情報
//user : リスナー情報(つまりこのツールによるログインアカウント情報)
//rtmp : ストリーミング配信情報
//ms : サーバ情報(ここから<addr>, <port>, <thread>を取得する)
//twitter: Twitter連携情報
//player : エラー情報?不明
//<?xml version="1.0" encoding="utf-8"?>
//<getplayerstatus status="ok" time="1293429226">
// <stream>
// <id>lv********</id>
// <watch_count>0</watch_count>
// <title>【初見】○○の雑談枠【歓迎】</title>
// <description>雑談枠です。初見さん歓迎ノ</description>
// <comment_count>0</comment_count>
// <danjo_comment_mode>0</danjo_comment_mode>
// <hqstream>0</hqstream>
// <nicoden>0</nicoden>
// <relay_comment>0</relay_comment>
// <park>1</park>
// <bourbon_url></bourbon_url>
// <full_video></full_video>
// <after_video></after_video>
// <before_video></before_video>
// <kickout_video></kickout_video>
// <header_comment>0</header_comment>
// <footer_comment>0</footer_comment>
// <plugin_delay></plugin_delay>
// <plugin_url></plugin_url>
// <plugin_urls/>
// <provider_type>community</provider_type>
// <default_community>co******</default_community>
// <archive>0</archive>
// <is_dj_stream>0</is_dj_stream>
// <twitter_tag>#eternalplace25</twitter_tag>
// <is_owner>0</is_owner>
// <owner_id>******</owner_id>
// <is_reserved>0</is_reserved>
// <base_time>1293429118</base_time>
// <open_time>1293429118</open_time>
// <end_time>1293431025</end_time>
// <start_time>1293429225</start_time>
// <telop>
// <enable>0</enable></telop>
// <ichiba_notice_enable>0</ichiba_notice_enable>
// <comment_lock>0</comment_lock>
// <background_comment>0</background_comment>
// <contents_list>
// <contents id="main" disableAudio="0" disableVideo="0" start_time="1293429120">rtmp:rtmp://nlpoca28.live.nicovideo.jp:1935/publicorigin/01/,lv35877379</contents></contents_list>
// <press>
// <display_lines>-1</display_lines>
// <display_time>-1</display_time>
// <style_conf/></press>
// <is_priority_prefecture></is_priority_prefecture>
// </stream>
//
// <user>
// <room_label>co******</room_label>
// <room_seetno>0</room_seetno>
// <userAge>3</userAge>
// <userSex>1</userSex>
// <userPrefecture>99</userPrefecture>
// <nickname>ロフトくん</nickname>
// <is_premium>1</is_premium>
// <user_id>14174847</user_id>
// <hkey></hkey>
// <is_join></is_join>
// <immu_comment>0</immu_comment>
// <can_broadcast>0</can_broadcast>
// <can_forcelogin>0</can_forcelogin>
// <twitter_info>
// <status>disabled</status>
// <after_auth>0</after_auth>
// <screen_name></screen_name>
// <followers_count>0</followers_count>
// <is_vip>0</is_vip>
// <profile_image_url>http://s.twimg.com/a/******/images/default_profile_6_normal.png</profile_image_url>
// <tweet_token>b6c1c673652a5471df98ca3c4909860c14377caa</tweet_token></twitter_info>
// </user>
//
// <rtmp is_fms="1" rtmpt_port="80">
// <url>rtmp://nleza08.live.nicovideo.jp:1935/liveedge/live_101227_14_1</url>
// <ticket>******:lv******:0:******:80dfb64e4a643b7b</ticket>
// </rtmp>
//
// <ms>
// <addr>msg102.live.nicovideo.jp</addr>
// <port>2814</port>
// <thread>1062357715</thread>
// </ms>
// <tid_list/>
//
// <twitter>
// <live_enabled>1</live_enabled>
// <vip_mode_count>10000</vip_mode_count>
// <live_api_url>http://watch.live.nicovideo.jp/api/</live_api_url>
// </twitter>
//
// <player>
// <error_report>1</error_report>
// </player>
//</getplayerstatus>
//XML解析
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(xml);
XmlElement root = xdoc.DocumentElement;
//番組情報
XmlNodeList stream = root.GetElementsByTagName("stream");
//m_base_time = stream[0].ChildNodes[28].InnerText; //仕様変更のたびにずれるのでぼつ
foreach (XmlNode node in stream[0].ChildNodes)
{
if (node.Name == "base_time")
{
m_base_time = node.InnerText;
}
}
//user情報
//m_userid = user[0].ChildNodes[7].InnerText;//仕様変更のたびにずれるのでぼつ
XmlNodeList user = root.GetElementsByTagName("user");
foreach (XmlNode node in user[0].ChildNodes)
{
if (node.Name == "user_id")
{
m_userid = node.InnerText;
}
}
//サーバ情報
XmlNodeList ms = root.GetElementsByTagName("ms");
//余談だが、ms.Countは常に0と想定し、ms[0]のみ読む。
m_ComSrvAddr = ms[0].ChildNodes[0].InnerText;
m_ComSrvPort = ms[0].ChildNodes[1].InnerText;
m_ComSrvThread = ms[0].ChildNodes[2].InnerText;
//表示
Console.WriteLine();
Console.WriteLine("○ユーザ情報");
Console.WriteLine(" ユーザID " + m_userid);
Console.WriteLine("○コメントサーバ情報");
Console.WriteLine(" アドレス " + m_ComSrvAddr);
Console.WriteLine(" ポート " + m_ComSrvPort);
Console.WriteLine(" スレッドID " + m_ComSrvThread);
Console.WriteLine();
}
catch (Exception e)
{
Console.WriteLine();
Console.WriteLine("×コメントサーバ情報");
Console.WriteLine(" 情報取得失敗");
Console.WriteLine("Message :n" + e.Message);
Console.WriteLine("Type :n" + e.GetType().FullName);
Console.WriteLine("StackTrace :n" + e.StackTrace.ToString());
return false;
}
return true;
}
static bool GetCommnet()
{
try
{
//ホスト名からIPアドレスを取得
IPAddress hostadd = Dns.GetHostEntry(m_ComSrvAddr).AddressList[0];
//IPEndPointを取得
IPEndPoint ephost = new IPEndPoint(hostadd, int.Parse(m_ComSrvPort));
//Socketの作成
System.Net.Sockets.Socket sock = new System.Net.Sockets.Socket(
System.Net.Sockets.AddressFamily.InterNetwork,
System.Net.Sockets.SocketType.Stream,
System.Net.Sockets.ProtocolType.Tcp);
//接続
sock.Connect(ephost);
//コメントリクエストメッセージを送信
//注)最後に'\0'を挿入しないとレスポンスは返ってこない
string param = String.Format("<thread thread=\"{0}\" version=\"20061206\" res_from=\"-100\"/>\0", m_ComSrvThread);
byte[] data = Encoding.UTF8.GetBytes(param);
sock.Send(data, data.Length, System.Net.Sockets.SocketFlags.None);
//受信する
const int MAX_RECEIVE_SIZE = 1024;
string prev = "";
while (true)
{
byte[] resBytes = new byte[MAX_RECEIVE_SIZE];
int resSize = sock.Receive(resBytes, resBytes.Length, System.Net.Sockets.SocketFlags.None);
if (resSize == 0)
{
break;
}
string xml = prev + Encoding.UTF8.GetString(resBytes, 0, resSize);
//Console.WriteLine("○データ受信(" + resSize.ToString() + "byte)");
//Console.WriteLine(xml);
//Console.WriteLine();
//XML解析
//<thread hoge />\0<chat>情報</chat>\0<chat>情報</chat>\0の形で受信する
//<thread hoge />は捨て、<chat>情報</chat>から情報を取り出す
xml = xml.Replace('\0', '\n');
string[] lines = xml.Split('\n');
foreach (string line in lines)
{
Console.WriteLine(line);//受信XML表示(1行)
if (line != "" && !line.EndsWith(">"))
{
//MAX_RECEIVE_SIZEいっぱいに受信した場合等
//XMLが閉じていない場合は次回Receive時結合する
prev = line;
break;
}
//<thread hoge="hoge">は初回送られてくる
if (line.StartsWith("<thread"))
{
//チケット、コメントサーバー時刻取得
//<thread ticket="チケット" server_time="サーバー時刻" hoge="hoge">を取得
string ticket = "";
string server_time = "";
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(line);
XmlElement root = xdoc.DocumentElement;
foreach (XmlAttribute attrib in root.Attributes)
{
if (attrib.Name == "ticket")
{
ticket = attrib.Value;
}
if (attrib.Name == "server_time")
{
server_time = attrib.Value;
}
}
if ((ticket == "") || (server_time == ""))
{
return false;
}
//コメント処理開始時刻
m_DateTimeStart = DateTime.Now;
//vpos(放送経過時間[sec]*100)を算出
//コメントサーバ開始時間
Int64 serverTimeSpan = Int64.Parse(server_time) - Int64.Parse(m_base_time);
//コメント投稿時間(1コメゲッターなのでここでは0secですね)
Int64 localTimeSpan = GetUnixTime(DateTime.Now) - GetUnixTime(m_DateTimeStart);
string vpos = ((serverTimeSpan + localTimeSpan) * 100).ToString();
//postkeyを取得
if (!GetPostKey())
{
return false;
}
//コメント投稿(1コメ)
//注)最後に'\0'を挿入する
param = String.Format("<chat thread=\"{0}\" ticket=\"{1}\" vpos=\"{2}\" postkey=\"{3}\" mail=\" 184\" user_id=\"{4}\" premium=\"1\">わくおつ</chat>\0"
, m_ComSrvThread
, ticket
, vpos
, m_postkey
, m_userid);
data = Encoding.UTF8.GetBytes(param);
sock.Send(data, data.Length, System.Net.Sockets.SocketFlags.None);
Console.WriteLine("○投稿します");
Console.WriteLine(param);
}
if (line.StartsWith("<chat_result"))
{
//コメント投稿結果
//<chat>ここ</chat>を取り出す
Console.WriteLine("(投稿応答)");
Console.WriteLine();
}
if (line.StartsWith("<chat "))
{
//コメント取得
//<chat>ここ</chat>を取り出す
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(line);
Console.WriteLine(xdoc.InnerText);
}
}
}
}
catch (Exception e)
{
Console.WriteLine("Message :n" + e.Message);
Console.WriteLine("Type :n" + e.GetType().FullName);
Console.WriteLine("StackTrace :n" + e.StackTrace.ToString());
return false;
}
return true;
}
//Unixタイムに変換
public static Int64 GetUnixTime(DateTime targetTime)
{
TimeSpan elapsedTime = targetTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return (Int64)elapsedTime.TotalSeconds;
}
static bool GetPostKey()
{
try
{
//注)
//block_no = 最新コメント番号(レス番) ÷ 100
//らしい。1コメゲッタなので0にしてる。
string url = "http://live.nicovideo.jp/api/getpostkey?thread=" + m_ComSrvThread + "&block_no=0";
//HTTP GET リクエストの作成
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.CookieContainer = m_cc;//取得済みのクッキーコンテナ
WebResponse res = req.GetResponse();
Stream resStream = res.GetResponseStream();
StreamReader sr = new StreamReader(resStream, Encoding.UTF8);
string text = sr.ReadToEnd();
sr.Close();
resStream.Close();
//応答はプレーンテキストで
//postkey=ergerwhg54hy4wfwegrghg
//のように返ってくる
m_postkey = text.Substring(8, text.Length - 8);
}
catch (Exception e)
{
Console.WriteLine("Message :n" + e.Message);
Console.WriteLine("Type :n" + e.GetType().FullName);
Console.WriteLine("StackTrace :n" + e.StackTrace.ToString());
return false;
}
return true;
}
}
}

ピンバック:ニコ生アラート + コメントビューア + 1コメゲット ツールを作る(C#) その1 « 夏研ブログ
お世話になっております。
お返事遅れましたが、きちんと動作しております!
ありがとうございました!
要望というか、技術的に可能かお聞きしたいのですが
例えば特定のタグ・放送タイトルをつけている生放送に絞って1GETすることなどは可能でしょうか??
>#1 by unknownさん
こんにちわ。無事動作とのことよかったです。
放送タイトルは取得可能です。
関数GetPlayerStatusで取得しているXMLに含まれています。
string xmlにgetplayerstatus(ニコ生API)の戻り値のXML全文を格納しています。
(XMLの構造は、関数GetPlayerStatusのコメントに記載しています。)
放送タイトルは、このXMLの<stream>タグ内の、<title>放送タイトル</title>に入っています。
取り出し方は<usr>タグの<user_id>ユーザID</usr_id>の取り出し処理と同様になります。
現在は、コミュID、生主IDにマッチした放送に対して、GetPlayerStatusを実施していますが、
これを全番組に対して実施するよう変更し、放送タイトルを監視する改造が必要になります。
放送タイトルと、任意キーワードとのマッチングは、以下のように実装できます。
string title = 取り出した放送タイトル;
if(title.Contains(“キーワード”))
{
//キーワードにマッチしたのでこの放送に1コメ送信
}
放送のタグについては、今のところ取得できるAPIがないようです。
(放送カテゴリ(“一般”、”ゲーム”、”やってみた”等)であれば、
ニコニコ生放送のRSS http://live.nicovideo.jp/recent/rss に載っており、
<category>ゲーム</category>部分を抜き出すことで取得できます。
PHPでの実装法を http://c-loft.com/blog/?p=598 に記載しています。)
ご参考になれば幸いです。
放送のタグですが、APIがない現状では、放送ページのHTMLから取得するのが正攻法でしょうか、
以下のようなHTMLになっているようです。
<a class=”nicopedia” rel=”tag” href=”大百科URL” rel=”nofollow”>タグ名</a>
放送タグについて、追加情報です。
APIでは無いですが、以下ページにアクセスすると
検索結果のHTMLが返ってくるようです。
http://live.nicovideo.jp/search/検索ワード?kind=tags
定期的に検索をかけ、返ってきたHTMLから、
視聴したい番組名やコミュ名、コミュID等でマッチングを
行うことでアラートツールが作れそうですね。
サイトのHTML文字列を取得するサンプルは以下に記載しております。
http://c-loft.com/blog/?p=637
はじめましてコミュニティから来ました。
コンパイルしてみたんですが
このようなエラーが返ってきてコンパイル出来ないのですがどうすればいいのでしょうか?
source.cs(157,40): error CS1002: ; が必要です。
source.cs(223,98): error CS1026: ) が必要です。
source.cs(243,40): error CS1002: ; が必要です。
source.cs(326,40): error CS1002: ; が必要です。
source.cs(398,40): error CS1002: ; が必要です。
source.cs(522,57): error CS1026: ) が必要です。
source.cs(561,40): error CS1002: ; が必要です。
source.cs(892,40): error CS1002: ; が必要です。
お時間が相手いる時で結構ですのでよろしくお願いします。
>#5 by 名無しさん
こんにちわ。お返事遅れてごめんなさい。
文法エラーの指摘ですので、ソースが書き換わっている可能性があります。
ソースコード欄右上に、クリップボードにコピーするボタンを設置しました、
(マウスオーバーで出現します)、一発でコピーできますので使ってみてください。
返事ありがとうございます
157行目のところで”の中に”があるの事でエラーをだしているようです。
コンパイラはVisualStudio 2010、VisualStudio 2008、csc.exeを使ってみましたが同じ結果でした。
なんども質問してすみません
>#7 by 名無しさん
こんにちわ。
エラー箇所は文字列中、文字eがある箇所に一致しているようですね。原因は分かりませんが文字コード周りかな?
試しにeを含まない適当な文字列(または空文字””)に変えてビルドしてみてください。
例) string url = “niconico”;
上記でビルドが通りましたら改めて正しい文字列にしてみて下さい(該当箇所はコピペでなく、手打ちでの入力がいいかもしれません)。
すいません解決しました。
エラーになってた理由は
Google Chromeを使っていたせいでした
Chromeでソースコード欄のURLの部分がstring url = “<a href="http://live.nicovideo.jp…とタグごと表示されていたからでした
なんども質問してすみませんでした
>#9 by 名無しさん
こんにちわ。確かにChromeだとHTMLタグが入りますね。
ソースコードが書き換わるなんて・・・ (ToT)
ブログの設定で直せないかなー、いじってみます。
ご報告ありがとうございました。
はじめまして
クッキーの処理について検索して、こちらのページまでこさせてもらいました。
「クッキー取得ライブラリ ver3」の使用方法大変わかり易く重宝しました。
ありがとうございます。
さて、クッキー取得ライブラリを使わさせていただいて少し疑問点が生じたので、よろしければご教授お願いいただけるでしょうか。
疑問というのは、
m_cc = new CookieContainer();
m_cc.Add(user_session);
上のコードなのですが、例えば、
CookieCollection collection = cookieGetter.GetCookieCollection(new Uri(“http://live.nicovideo.jp/”));
の”http://live.nicovideo.jp/” を ”http://www.google.co.jp/”と変更して、他のクッキーを取得することも可能なのでしょうか?
いろいろ試してみたのですが、「CookieGetterSharp.dll」自体がニコニコ動画アドレスしか対応していないのかなと思いました。
お暇なときでよろしいので、お教え願えますでしょうか。
それでは失礼いたします。
>#11 by あさきゆめみし さん
こんばんわ。
お好きなURLを指定できるようです。
http://d.hatena.ne.jp/halxxxx/20091212/1260649353
に、ブラウザ、URL、名前を指定してクッキーを取得するサンプルアプリが紹介されています。
なおFirefox4など、最近登場したブラウザへの対応を、
http://com.nicovideo.jp/community/co235502
こちらのコミュニティオーナーの方が実施されています(CookieGetterSharpDistribution20110517)。
おはようございます
クッキーの取得できました。
ロフトさんがどんなURLでもクッキーを取得できると教えてくれなければ、諦めてかけてました。ありがとうございます。
最新ブラウザ対応については、今朝ダウンロードしました。
とりあえず、今のコードにブラウザのクッキー取得を実装してから改めて最新のものにしたいと思います。
最後に本当にありがとうございました。
今後とも、ソフトウェア開発頑張ってください。
ロフトさんのご活躍を期待しております。
>#13 by あさきゆめみし さん
無事クッキー取得できたとのこと、よかったです。
なお、ライブラリをCookieGetterSharpDistribution20110517に入れ替えると、
BrowserTypeの番号が変わります(Firefox4とかが増えてます。)
番号の定義は、VisuauStudioでコード中の”BrowserType”を選択、右クリック->定義へ移動で 見れます。
すばらしいライブラリの開発者に感謝ですね。ぜひぜひツール作りに役立てて下さい。
はじめまして。
C#の勉強を兼ねて、ここのソースに少し手を加えたので、私のブログに記事を書こうと思ってます。
ゆくゆくは配布までしたいのですが、
さすがにロフトさんの許可がなければ配布できまないと思いまして。
記事には、
・ここのソースを参考にした事
・ここには迷惑をかけない事
を書こうと思います。
急ぎませんので、返事いただけるとありがたいです。
ニコ生アラート + コメントビューア + 1コメゲット ツールを作る(C#) その2
>えりのあさん
こんにちは、記事をご覧いただきありがとうございます。
つたないソースですが、参考になりましたら幸いです。
ソースコード改造、ブログへの掲載、
ツールとしての公開はOKです。
特に引用・参考元の表記は強制致しませんが、
リンク形式で紹介頂くと喜びます。
不具合等による問題の責任は免責でお願い致します。
ニコニコ系ツール・プログラミング関連情報充実の
お力添えになれましたら幸いですノ
お返事ありがとうございます。
自己責任で行います。
コンパイルすると、
エラー:型または名前空間’Hel’が見つかりませんでした。usingディレクトリまたはアセンブリ参照が不足しています。
と出ます。
プログラミング初心者なので自分なりに調べましたが解決できてません。
プログラムは、エラーが出たところを少し書きなおしただけです。ほかは書いてある通りにしてます。
何か解決策ありますか?
>はなさん
こんにちは。
CookieGetterSharp.dllを参照に追加されていますでしょうか?
参照に追加されていない場合、そのようなエラーが出ます。
>エラー:型または名前空間’Hel’が見つかりませんでした。
>usingディレクトリまたはアセンブリ参照が不足しています。
ちなみに’Hel’ではなく’Hal’です。
9行目(using Hal.CookieGetterSharp;)の記述が
あっているか確認してみてください。
参照設定もやってます。
“Hel” はタイピングミスです。すみません。
(using Hal.CookieGetterSharp;)の記述もあってます。
何がいけないのでしょう?
>はなさん
こんにちは。
名前空間のエラーなので、参照設定ができているかの確認が必要そうです。
後述の確認をしてみてください。
(VisualStudioでの開発の場合の確認事項です。)
インテリセンスが効き、オブジェクトブラウザでdllの中が見えれば参照はできていると思います。
・インテリセンスによる確認
ソースコードのどこかで(例えばmain関数の中で)、
Hal.CookieGetterSharp.
と打って、
CookieGetterSharp.の.(ドット)の後ろで、Ctrl + Spaceを押して、
インテリセンスが効くか確認してみて下さい。
Hal.CookieGetterSharp.BrowserType
Hal.CookieGetterSharp.CookieGetter
のように、候補が表示されれば正常に参照設定できています。
候補が表示されなければ参照設定できていません。
・オブジェクトブラウザによる確認
ソリューションエクスプローラ->参照設定 をダブルクリックで開き、
“CookieGetterSharp”があるかどうか確認してみてください。
ある場合、”CookieGetterSharp”の文字をダブルクリックしてみて下さい。するとオブジェクトブラウザが起動します。
オブジェクトブラウザで”CookieGetterSharp”の中身のクラスを
のぞき見(Hal->CookieGetterSharp->BrowserType,CookieGetter等の一覧が見える)できれば参照設定はできています。
こんばんは。
返信がとても遅れてしまい申し訳ありません。
コンパイラでの参照の仕方が違ったみたいでなんとか
動くようになりましたが・・・
490行あたりの
int resSize = sock.Receive(resBytes, resBytes.Length, System.Net.Sockets.SocketFlags.None);
で、これ以上先に進まず、動かなくなります(動いてないように見えます)。
解決方法ありますか?
度々、すみません。
>はなさん
こんにちは。コンパイル通ってよかったです。
sock.Receiveは接続先(ニコ生鯖)からパケットを受信するまで待ち続けるため、
止まって見えることはあります。
490行あたりとのことなので、GetProgramInfo()内のsock.Receiveだと思いますが
これは番組開始情報を受信するまで待ち続けます。
放送枠数が少ない時間帯だと止まって見えることもありますが、
夜のゴールデンタイムなど放送数が多い時間帯なら止まることはないです。
まずは放送数の多い時間帯で試してみて下さい。
また、事前の処理GetAlertInfo()にて、
ニコ生鯖のアドレス(m_addr)、ポート(m_port)、スレッドID(m_thread)が
取得できているか確認してみて下さい。
GetAlertInfo()の結果として、例えば以下のようなログが表示されます。
○サーバ情報
アドレス twr02.live.nicovideo.jp
ポート 2531
スレッドID 1000000013
追記:
ソースコードの文字列中のエスケープ文字が消えていたので修正しました。
\n → n 、 \” → ” になっていました。
こんにちは。
VBNETで使用しようと試行錯誤しています。
質問なのですが、CookieGetterSharp.dllは現在でも利用できますか?
どうも上手く取得できないようなので・・・
>佐々木さん
こんにちは。
halxxxx氏が改造&公開されていたCookieGetterSharp.dllは
開発が停止しているため、最近のブラウザ(Firefox4以降等)には
対応していません。
ニコニコミュニティ悠悠閑閑前途遼遠のコミュ主であるうつろ氏が
最近のブラウザへの対応改造&公開されていますので、
そちらを使用されることをお勧めします。
なお、halxxxx氏のCookieGetterSharp.dllとは、
BrowserTypeの定義値が変わりますので、
配布されているdllを参照して確認して下さい。
(ブログ本文もうつろ氏のCookieGetterSharp.dllをメインに書きなおした方がいいですね。。)
ロフトくんさん、返信有難うございます。
よく見たら追記で書いていましたね。
よく確認せずすみませんでした。
いろいろとやってみたいと思います。
>佐々木さん
できるだけ記事の内容も最新情報に更新した方がいいですよね、
うつろ氏のCookieGetterSharp.dllがメインになるよう、
更新したいと思います。
うつろ氏のCookieGetterSharp.dllをメインに書きなおしました。
ロフトくんさん、こんにちは。
うつろ氏のCookieGetterSharp.dllをメインで書いて頂き有難うございます。
助かります。
現在、VB2010に移植中なのですが、Consoleではなく、デザイナでフォーム上に作成しています。
そこで初歩的な質問いいでしょうか?
Private Shared Sub Main(ByVal args As String())
mainの引数に当たるargsは何にあたりますでしょうか?
いままで、Consoleアプリを作ったことが無いので、少し戸惑っています・・・
>佐々木さん
こんにちは。
main関数の引数について簡単に解説します。
main関数の引数はコマンドライン引数と呼ばれるものです。
コマンドライン引数とは、ツールを起動する際に、
ツールの外部からツールに渡すことができる文字列です。
渡された文字列を元に処理を切り替えるような実装をしておくことで、
動作モードの切り替え等に利用できます。
以下、サンプルです。
'コンソールアプリケーションVBConsoleTest Module Module1 Sub Main(ByVal args() As String) For Each arg In args System.Console.WriteLine(arg) Next End Sub End Module上記をビルド後、コマンドプロンプトを開き、cdコマンドで、
exe(VBConsoleTest.exe)のあるフォルダに移動します。
普通にexeを起動しただけでは何も表示されません。
コマンドライン引数を指定して起動すると、表示されます。
外部から与えた文字列をツール内部で取得できていることが分かります。
ブログ本文に掲載しているツールのコマンドライン引数は、
以下のパターンで実装しています。
コマンドライン引数は、特に言語(c/c++/C#/VB/java等)によらず(文法は変わりますが)、
main関数の引数として実装可能な仕組みです。
詳細は書籍やGoogle先生などを参考にして下さい。
>佐々木さん
また、コマンドライン引数は、コンソールアプリケーション(CUI)にも
Windowsフォームアプリケーション(GUI)にも、
外部から呼び出す際に与えることができ、
ツール内部から取得も可能です。
詳しくは
http://dobon.net/vb/dotnet/programing/commandline.html
を参照されてください。
GUIアプリでコマンドライン引数を取得する場合、
「’コマンドラインを配列で取得する」の方法がお勧めです。
ただ、GUIアプリであれば、コマンドライン引数を使わなくても、
フォームにチェックボックスやテキストボックスを配置するなどして、
画面上で機能を選択できるようにする方が使いやすいかもしれません。
ロフトくんさん、分かりやすく解説して頂き有り難うございます。
なんとかVB2010でも作れそうです。
これから移植?コードを書いていきたいと思います。
完成しましたら、また報告させて頂きます。
すみません、もう一つ質問させて下さい。
このツール.exe co15655xx ←例です。
で、コミュを指定し起動すると、いろんな番組の○データ受信を取り続けてしまいます。
○データ受信(241byte)
7593979x,co121302x,1637647x
放送 : 7593979x
コミュ : co121302x
生主 : 1637647x
○データ受信(118byte)
7593979x,co115771x,207980x
放送 : 7593979x
コミュ : co115771x
生主 : 207980x
○データ受信(119byte)
7593980x,co141729x,1029938x
放送 : 7593980x
コミュ : co141729x
生主 : 1029938x
○データ受信(119byte)
7593977x,co145859x,2528826x
放送 : 7593977x
コミュ : co145859x
生主 : 2528826x
○データ受信(119byte)
7593981x,co130316x,2103013x
放送 : 7593981x
コミュ : co130316x
生主 : 2103013x
このツール.exe
で、そのまま起動させると、、BrowserTypeではFirefoxを設定しているのですが、IEが立ち上がり、たぶん一番最新の放送を拾ってきて、コメントを拾っていきます。
(通常使うブラウザをIEにしているのでそのせいかも知れません)
○データ受信(240byte)
7593990x,co142734x,411513x
放送 : 7593990x
コミュ : co142734x
生主 : 411513x
○ユーザ情報
ユーザID 142997x
○コメントサーバ情報
アドレス msg103.live.nicovideo.jp
ポート 281x
スレッドID 114296431x
○投稿します
わくおつ
何か設定などが間違っていますでしょうか?
すみません、もう一つ質問させて下さい。
このツール.exe co15655xx ←例です。
で、コミュを指定し起動すると、いろんな番組の○データ受信を取り続けてしまいます。
○データ受信(241byte)
7593979x,co121302x,1637647x
放送 : 7593979x
コミュ : co121302x
生主 : 1637647x
○データ受信(118byte)
7593979x,co115771x,207980x
放送 : 7593979x
コミュ : co115771x
生主 : 207980x
○データ受信(119byte)
7593980x,co141729x,1029938x
放送 : 7593980x
コミュ : co141729x
生主 : 1029938x
○データ受信(119byte)
7593977x,co145859x,2528826x
放送 : 7593977x
コミュ : co145859x
生主 : 2528826x
○データ受信(119byte)
7593981x,co130316x,2103013x
放送 : 7593981x
コミュ : co130316x
生主 : 2103013x
.
.
.
このツール.exe
で、そのまま起動させると、、BrowserTypeではFirefoxを設定しているのですが、IEが立ち上がり、たぶん一番最新の放送を拾ってきて、コメントを拾っていきます。
(通常使うブラウザをIEにしているのでそのせいかも知れません)
○データ受信(240byte)
7593990x,co142734x,411513x
放送 : 7593990x
コミュ : co142734x
生主 : 411513x
○ユーザ情報
ユーザID 142997x
○コメントサーバ情報
アドレス msg103.live.nicovideo.jp
ポート 281x
スレッドID 114296431x
○投稿します
わくおつ
何か設定などが間違っていますでしょうか?
>佐々木さん
こんにちは。
>このツール.exe co15655xx ←例です。
この場合、コミュ番号co15655xxの番組開始を検出するまで
データ受信を続けます。
コミュ番号co15655xxの番組開始を検出すると、
その番組に1コメを送信します。
>このツール.exe
この場合、最初に検出した放送に問答無用で1コメを送信します。
起動するブラウザはProcess.Start(URL)というコマンドで指定しており、この場合、標準のブラウザが起動します。
ブラウザを指定したい場合は、
Process.Start(@”C:\~~\firefox.exe”, URL);のように、
exeの場所を指定する必要があります。
ロフトくんさん、有難うございます。
了解致しました。
>佐々木さん
こんにちは。
いろいろと改善点のあるコードですが、是非便利ツール開発に役立てて下さいノ
こんにちは。
着実にコード作成進めています。
プログラムと関係ないのですが・・・・
ニコ生から送られてくるvposを、1/100秒でそのまま変換すると、放送の時間表示と違い、少し誤差が応じてくるのですが、何故か分かりますか?
>佐々木さん
こんにちは。
私もvposから出すのかと思っていましたが、
dateから出すと一致するようです。
ロフトくんさん、返信有難う御座います。
なるほどvposではなかったのですか。
ニコ生も間際らしいですね。
おかげで経過時間の取得が出来ました。
あとは、
1.返ってくるpremiumのNoが8番(iPhone/iPod)、9番(携帯)の場合は、一般なのかプレミアムなのかの判断をどうやって考えるのか
2.DataGridViewへの高速追加が可能なのか(1000コメ数を取得する場合かなりの時間がかかる為)
3.184ではない場合、IDから名前を取得する方法
4.コテハンの登録方法
ぐらいでしょうか。
まだ、時間がかかりそうですが、完成しましたらこちらでも報告させて頂きます。
有難うございます。
>佐々木さん
こんにちは、本格的なコメビュ実装をされているのですね、
応援しております。
1, 3についてはベストな解ではないかもしれませんが、
ユーザのマイページのHTMLからのスクレイピング(抜き出し)で取得は可能そうです。
以下は、あるツール用に実装した、マイページからサムネURL文字列をスクレイピングする関数です。
ひとつの選択肢としてご参考になれば幸いです。
マイページはアクセス制限があり、”短時間での連続アクセスはご遠慮ください”と怒られる場合があるので、
制限に引っかからないようにする必要があります。
//ユーザーサムネURL取得 static string GetUserImageURL(string userID) { string url_image = ""; try { string url = "ht"+"tp://www.nicovideo.jp/user/" + userID; //HTTP GET リクエストの作成 HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); req.CookieContainer = m_cc;//取得済みのクッキーコンテナ WebResponse res = req.GetResponse(); Stream resStream = res.GetResponseStream(); StreamReader sr = new StreamReader(resStream, Encoding.UTF8); string text = sr.ReadToEnd(); sr.Close(); resStream.Close(); //HTMLから文字列抽出 string regular = "<div id=\"accountFace\"><img src=\"(?<tag_image>.*?)\" alt="; string regular_ng = "短時間での連続アクセスはご遠慮ください"; Regex re = new Regex(regular, RegexOptions.IgnoreCase | RegexOptions.Singleline); for (Match m = re.Match(text); m.Success; m = m.NextMatch()) { //サムネURL抽出 url_image = m.Groups["tag_image"].Value; } Regex re_ng = new Regex(regular_ng, RegexOptions.IgnoreCase | RegexOptions.Singleline); for (Match m = re_ng.Match(text); m.Success; m = m.NextMatch()) { //アクセス制限 return "ng"; } return url_image; } catch (Exception e) { Console.WriteLine("Message :\n" + e.Message); Console.WriteLine("Type :\n" + e.GetType().FullName); Console.WriteLine("StackTrace :\n" + e.StackTrace.ToString()); return ""; } }2については私も研究中です。
データとDataGridViewを関連付けることで、自動表示できそうな気がするのですが・・
(http://c-loft.com/blog/?p=1375 にて研究開始) 良い方法があれば教えて下さい^^
4についてはコテハン情報(ユーザID, コテハン)のファイル保存方式として、データベースファイルあるいはXMLファイルとして保存が良いのではないかと思います。
データベースとしては、.NETアプリケーションから手軽に使える軽量データベースとしてSystem.Data.SQLiteを使い始めたのですが便利そうです。
(とっかかりの記事を http://c-loft.com/blog/?p=1497 に書いています。)
XML形式の保存には、オブジェクトのXMLシリアライズ(クラスのインスタンス→XML化)・デシリアライズ(XML→クラスのインスタンス化)が便利です。
私はアプリの設定情報をもたせたクラスをシリアライズでXMLファイル保存・デシリアライズでXMLファイルから設定読み込みで使っています。
また、XMLから欲しい情報(あるユーザIDのコテハンを)を抽出する方法として、(私は現状未習得ですが、)LINQ to XML によるクエリ発行という方法が”今風”で便利らしいです^^
追記
ソースコード中、変数string urlの文字列( http://www.nicovideo.jp/ ~)に自動的にハイパーリンクのタグが付いてしまっていたので、
わざと文字列を分割してます;___; ( ht + tp://www.nicovideo.jp/~)
便利機能ではあるけど、ソースコードはいじらないで欲しいですね^^
ロフトくんさん、重ね重ね有難うございます。
おかげで早く完成しそうです。m(_ _)m
実は自分はずーとVB2005を使っていたのですが、これを機会にVB2010で作ろうと思い試しています。
http://c-loft.com/blog/?p=1497も見て勉強させて頂きたいと思います。
XML関連は、かなり便利になっているようですね。
便利なものは勉強して使わなといけないとは思っているのですが、なかなか・・・(^^;
あと自分が入ってるコミュだと、コメント出来ない現象とかありますか?
自分が入っていない放送だとコメント出来て、自分がコミュに入っている放送だとコメント出来ないという現象が出ましたので・・・
ただこれは、自分がC#からVBに変更したおり間違ったのかなとも思っています。
>佐々木さん
こんにちは。
参加コミュの放送で試して見ましたが起きませんでした、
発生頻度はどれくらいでしょうか??
現象としては、コメントをsock.Sendしても、ニコ生側から応答がない状態でしょうか??それとも異常応答が返ってきますか??
正常にコメントできた場合は以下のようなstatus=”0″が返ってくると思います。
こんにちは。
確認した結果、
status=”4″
と返ってきています。
ただし、これは、最初に作ったプログラムはちゃんと投稿できていて、かなり汚く作っていたので、新しくプロジェクトを作成し、整理して作ったコードでは、status=”4″が返ってくるという感じになっています。
投稿部分のコードは、ココピペで何も変えていないので、さて何処がおかしいのだろうと考えているところです。
今のところ思いつくのは、Form1_Loadに、
BackgroundWorkerでのエラーを無効にする為、
Control.CheckForIllegalCrossThreadCalls = False
や、
DataGridViewのちらつき防止のため、
ダブルバッファしているぐらいでしょうか・・・
myType.GetProperty(“DoubleBuffered”
同じコードなのに、どこがいけないのか・・・
原因を追求中です・・・
まだ原因はつかめていませんが、
間違ったパラメータでコメント送信するとstatus=”4″が返ってくるらしいです。
うーん、2つのブロジェクトの投稿を比べてみても、パラメータは同じように思えるのですが・・・
ただ投稿エラーを起こすと、次の投稿を他の人とがすると、コメ番がずーとエラー箇所のコメ番になるので、そこらへんかなとも思っています。
ロフトくんさん、返信有難う御座います。
早速確認してみます。
>佐々木さん
こんにちは、うーん、パラメタですか、、
もし再現できるコードがあれば私も見てみますので教えて下さい。
ロフトくんさん、返信有難う御座います。
BackgroundWorkerなどは関係なかったようです。
どのパラメータが悪かったのか、詳しく分かれば少しは対処できるのですが・・・
かなり大雑把ですが、コードになります。(^^;
‘書き込みがうまくいかない
Dim cookieGetter__1 As ICookieGetter = CookieGetter.CreateInstance(BrowserType.Firefox)
Dim collection As CookieCollection = cookieGetter__1.GetCookieCollection(New Uri(“http://live.nicovideo.jp/”))
Dim user_session As Cookie = collection(“user_session”)
m_cc = New CookieContainer()
m_cc.Add(user_session)
‘Me.TextBox1.Text = (user_session.Value)
m_comID = “”
Dim liveID As String = lv_TextBox.Text
Dim url As String = “http://live.nicovideo.jp/api/getalertinfo”
‘XML取得
Dim wc As New System.Net.WebClient()
wc.Encoding = System.Text.Encoding.UTF8
Dim xml2 As String = wc.DownloadString(url)
wc.Dispose()
Console.WriteLine(“○認証API(getalertinfo)レスポンス取得”)
Console.WriteLine(xml2)
‘表示
‘上記の例として、以下のようなXMLが返ってくる、
‘サーバ情報は, , から取得する
‘
‘Anonymous
‘-CS4mPdPnMXL8cQJ2g4QakgwvuQ
‘
‘ twr02.live.nicovideo.jp
‘ 2533
‘ 1000000017
‘
‘
‘XML解析
Dim xdoc2 As New XmlDocument()
xdoc2.LoadXml(xml2)
Dim root As XmlElement = xdoc2.DocumentElement
Dim ms As XmlNodeList = root.GetElementsByTagName(“ms”)
‘余談だが、ms.Countは常に0と想定し、ms[0]のみ読む。
m_addr = ms(0).ChildNodes(0).InnerText
m_port = ms(0).ChildNodes(1).InnerText
m_thread = ms(0).ChildNodes(2).InnerText
‘表示
Debug.WriteLine(“○サーバ情報”)
Debug.WriteLine(” アドレス ” & m_addr)
Debug.WriteLine(” ポート ” & m_port)
Debug.WriteLine(” スレッドID ” & m_thread)
Console.WriteLine()
Dim url2 As String = “http://live.nicovideo.jp/api/getplayerstatus?v=” & liveID
‘string url = “http://live.nicovideo.jp/watch/lv” + liveID;
‘HTTP GET リクエストの作成
‘注)本APIから情報取得するには、ログインが必要っぽい
‘ しかし認証用API(要ログイン)はクッキーをくれなかった。
‘ そこで普通のログイン関数にてクッキーコンテナを取得、ここで使用している
Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url2), HttpWebRequest)
req.CookieContainer = m_cc
‘取得済みのクッキーコンテナ
‘req.CookieContainer.SetCookies(new System.Uri(“http://live.nicovideo.jp/”), m_UserSession.Name + “;” + m_UserSession.Value);
Dim res As WebResponse = req.GetResponse()
Dim resStream As Stream = res.GetResponseStream()
Dim sr As New StreamReader(resStream, Encoding.UTF8)
Dim xml As String = sr.ReadToEnd()
sr.Close()
resStream.Close()
‘TextBox2.Text = xml
Dim xdoc As New XmlDocument()
xdoc.LoadXml(xml)
Dim root2 As XmlElement = xdoc.DocumentElement
‘番組情報
Dim stream As XmlNodeList = root2.GetElementsByTagName(“stream”)
‘m_base_time = stream[0].ChildNodes[28].InnerText; //仕様変更のたびにずれるのでぼつ
For Each node As XmlNode In stream(0).ChildNodes
If node.Name = “base_time” Then
m_base_time = node.InnerText
End If
Next
‘user情報
‘m_userid = user[0].ChildNodes[7].InnerText;//仕様変更のたびにずれるのでぼつ
Dim user As XmlNodeList = root2.GetElementsByTagName(“user”)
For Each node As XmlNode In user(0).ChildNodes
If node.Name = “user_id” Then
m_userid = node.InnerText
End If
Next
‘サーバ情報
Dim ms2 As XmlNodeList = root2.GetElementsByTagName(“ms”)
‘余談だが、ms.Countは常に0と想定し、ms[0]のみ読む。
m_ComSrvAddr = ms2(0).ChildNodes(0).InnerText
m_ComSrvPort = ms2(0).ChildNodes(1).InnerText
m_ComSrvThread = ms2(0).ChildNodes(2).InnerText
‘表示
Debug.WriteLine(“○ユーザ情報”)
Debug.WriteLine(” ユーザID ” & m_userid)
Debug.WriteLine(“○コメントサーバ情報”)
Debug.WriteLine(” アドレス ” & m_ComSrvAddr)
Debug.WriteLine(” ポート ” & m_ComSrvPort)
Debug.WriteLine(” スレッドID ” & m_ComSrvThread)
Dim hostadd As IPAddress = Dns.GetHostEntry(m_ComSrvAddr).AddressList(0)
‘IPEndPointを取得
Dim ephost As New IPEndPoint(hostadd, Integer.Parse(m_ComSrvPort))
‘Socketの作成
Dim sock As New System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp)
‘接続
sock.Connect(ephost)
‘コメントリクエストメッセージを送信
‘注)最後に”を挿入しないとレスポンスは返ってこない
Dim param As String = [String].Format(“” & vbNullChar, m_ComSrvThread)
Dim data As Byte() = Encoding.UTF8.GetBytes(param)
sock.Send(data, data.Length, System.Net.Sockets.SocketFlags.None)
‘受信する
Const MAX_RECEIVE_SIZE As Integer = 1024
Dim prev As String = “”
While True
Dim resBytes As Byte() = New Byte(MAX_RECEIVE_SIZE – 1) {}
Dim resSize As Integer = sock.Receive(resBytes, resBytes.Length, System.Net.Sockets.SocketFlags.None)
If resSize = 0 Then
Exit While
End If
Dim xml3 As String = prev & Encoding.UTF8.GetString(resBytes, 0, resSize)
‘Console.WriteLine(“○データ受信(” + resSize.ToString() + “byte)”);
‘Console.WriteLine(xml);
‘Console.WriteLine();
‘XML解析
‘情報情報の形で受信する
‘は捨て、情報から情報を取り出す
xml3 = xml3.Replace(ControlChars.NullChar, ControlChars.Lf)
Dim lines As String() = xml3.Split(ControlChars.Lf)
For Each line As String In lines
Debug.WriteLine(line)
‘受信XML表示(1行)
If line “” AndAlso Not line.EndsWith(“>”) Then
‘MAX_RECEIVE_SIZEいっぱいに受信した場合等
‘XMLが閉じていない場合は次回Receive時結合する
prev = line
Exit For
End If
TextBox1.Text = xml3
If line.StartsWith(“<thread") Then
'チケット、コメントサーバー時刻取得
'を取得
Dim ticket As String = “”
Dim server_time As String = “”
Dim xdoc5 As New XmlDocument()
xdoc5.LoadXml(line)
Dim root5 As XmlElement = xdoc5.DocumentElement
For Each attrib As XmlAttribute In root5.Attributes
If attrib.Name = “ticket” Then
ticket = attrib.Value
End If
If attrib.Name = “server_time” Then
server_time = attrib.Value
End If
Next
If (ticket = “”) OrElse (server_time = “”) Then
End If
‘コメント処理開始時刻
m_DateTimeStart = DateTime.Now
‘vpos(放送経過時間[sec]*100)を算出
‘コメントサーバ開始時間
Dim serverTimeSpan As Int64 = Int64.Parse(server_time) – Int64.Parse(m_base_time)
‘コメント投稿時間(1コメゲッターなのでここでは0secですね)
Dim localTimeSpan As Int64 = GetUnixTime(DateTime.Now) – GetUnixTime(m_DateTimeStart)
Dim vpos As String = ((serverTimeSpan + localTimeSpan) * 100).ToString()
‘postkeyを取得
If Not GetPostKey() Then
End If
‘コメント投稿(1コメ)
‘注)最後に”を挿入する
param = [String].Format(“プログラムテスト。1” & vbNullChar, m_ComSrvThread, ticket, vpos, m_postkey, m_userid)
data = Encoding.UTF8.GetBytes(param)
sock.Send(data, data.Length, System.Net.Sockets.SocketFlags.None)
Debug.WriteLine(“○投稿します”)
Debug.WriteLine(param)
End If
If line.StartsWith(“<chat_result") Then
'コメント投稿結果
'ここを取り出す
Debug.WriteLine(“(投稿応答)”)
Console.WriteLine()
End If
'(“<chat ")にするとエラーが出るため変更
If line.StartsWith("chat ") Then
'コメント取得
'ここを取り出す
Dim xdoc6 As New XmlDocument()
xdoc6.LoadXml(line)
Debug.WriteLine(xdoc6.InnerText)
End If
Next
End While
GetUnixTimeクラスと、GetPostKey()は、変更せず、そのまま使用させていただいています。
>佐々木さん
こんにちは。
以下コードで、参加コミュ・非参加コミュに1コメ送信できたので差分等ないか確認してみて下さい。
(URL部分はかってにHTMLリンクタグが入るので、わざと”h” & “ttp://~”にして、回避してます。)
(ソースコード貼り付け時は[vb]~[/vb]で囲むと以下のようにハイライトされます。[ ]は半角です。)
Imports System.Net Imports Hal.CookieGetterSharp Imports System.Xml Imports System.IO Imports System.Text Module Module1 Dim m_ComSrvThread As String Dim m_cc As New CookieContainer() Dim m_postkey As String Sub Main() Dim cookieGetter__1 As ICookieGetter = CookieGetter.CreateInstance(BrowserType.Firefox) Dim collection As CookieCollection = cookieGetter__1.GetCookieCollection(New Uri("h" & "ttp://live.nicovideo.jp/")) Dim user_session As Cookie = collection("user_session") m_cc.Add(user_session) 'Me.TextBox1.Text = (user_session.Value) Dim m_comID As String = "" Dim liveID As String = "lv79302213" Dim url As String = "h" & "ttp://live.nicovideo.jp/api/getalertinfo" 'XML取得 Dim wc As New System.Net.WebClient() wc.Encoding = System.Text.Encoding.UTF8 Dim xml2 As String = wc.DownloadString(url) wc.Dispose() Console.WriteLine("○認証API(getalertinfo)レスポンス取得") Console.WriteLine(xml2) '表示 '上記の例として、以下のようなXMLが返ってくる、 'サーバ情報は, , から取得する ' 'Anonymous '-CS4mPdPnMXL8cQJ2g4QakgwvuQ ' ' twr02.live.nicovideo.jp ' 2533 ' 1000000017 ' ' 'XML解析 Dim xdoc2 As New XmlDocument() xdoc2.LoadXml(xml2) Dim root As XmlElement = xdoc2.DocumentElement Dim ms As XmlNodeList = root.GetElementsByTagName("ms") '余談だが、ms.Countは常に0と想定し、ms[0]のみ読む。 Dim m_addr As String = ms(0).ChildNodes(0).InnerText Dim m_port As String = ms(0).ChildNodes(1).InnerText Dim m_thread As String = ms(0).ChildNodes(2).InnerText '表示 Console.WriteLine("○サーバ情報") Console.WriteLine(" アドレス " & m_addr) Console.WriteLine(" ポート " & m_port) Console.WriteLine(" スレッドID " & m_thread) Console.WriteLine() Dim url2 As String = "h" & "ttp://live.nicovideo.jp/api/getplayerstatus?v=" & liveID 'string url = “h" & "ttp://live.nicovideo.jp/watch/lv” + liveID; 'h" & "ttp GET リクエストの作成 '注)本APIから情報取得するには、ログインが必要っぽい ' しかし認証用API(要ログイン)はクッキーをくれなかった。 ' そこで普通のログイン関数にてクッキーコンテナを取得、ここで使用している Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url2), HttpWebRequest) req.CookieContainer = m_cc '取得済みのクッキーコンテナ 'req.CookieContainer.SetCookies(new System.Uri(“h" & "ttp://live.nicovideo.jp/”), m_UserSession.Name + “;” + m_UserSession.Value); Dim res As WebResponse = req.GetResponse() Dim resStream As Stream = res.GetResponseStream() Dim sr As New StreamReader(resStream, Encoding.UTF8) Dim xml As String = sr.ReadToEnd() sr.Close() resStream.Close() 'TextBox2.Text = xml Dim xdoc As New XmlDocument() xdoc.LoadXml(xml) Dim root2 As XmlElement = xdoc.DocumentElement '番組情報 Dim stream As XmlNodeList = root2.GetElementsByTagName("stream") 'm_base_time = stream[0].ChildNodes[28].InnerText; //仕様変更のたびにずれるのでぼつ Dim m_base_time As String = "" For Each node As XmlNode In stream(0).ChildNodes If node.Name = "base_time" Then m_base_time = node.InnerText End If Next 'user情報 'm_userid = user[0].ChildNodes[7].InnerText;//仕様変更のたびにずれるのでぼつ Dim m_userid As String = "" Dim user As XmlNodeList = root2.GetElementsByTagName("user") For Each node As XmlNode In user(0).ChildNodes If node.Name = "user_id" Then m_userid = node.InnerText End If Next 'サーバ情報 Dim ms2 As XmlNodeList = root2.GetElementsByTagName("ms") '余談だが、ms.Countは常に0と想定し、ms[0]のみ読む。 Dim m_ComSrvAddr As String = ms2(0).ChildNodes(0).InnerText Dim m_ComSrvPort As String = ms2(0).ChildNodes(1).InnerText m_ComSrvThread = ms2(0).ChildNodes(2).InnerText '表示 Console.WriteLine("○ユーザ情報") Console.WriteLine(" ユーザID " & m_userid) Console.WriteLine("○コメントサーバ情報") Console.WriteLine(" アドレス " & m_ComSrvAddr) Console.WriteLine(" ポート " & m_ComSrvPort) Console.WriteLine(" スレッドID " & m_ComSrvThread) Dim hostadd As IPAddress = Dns.GetHostEntry(m_ComSrvAddr).AddressList(0) 'IPEndPointを取得 Dim ephost As New IPEndPoint(hostadd, Integer.Parse(m_ComSrvPort)) 'Socketの作成 Dim sock As New System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp) '接続 sock.Connect(ephost) 'コメントリクエストメッセージを送信 '注)最後に”を挿入しないとレスポンスは返ってこない Dim param As String = [String].Format("<thread thread=""{0}"" version=""20061206"" res_from=""-100""/>" & vbNullChar, m_ComSrvThread) Dim data As Byte() = Encoding.UTF8.GetBytes(param) sock.Send(data, data.Length, System.Net.Sockets.SocketFlags.None) '受信する Const MAX_RECEIVE_SIZE As Integer = 1024 Dim prev As String = "" While True Dim resBytes As Byte() = New Byte(MAX_RECEIVE_SIZE - 1) {} Dim resSize As Integer = sock.Receive(resBytes, resBytes.Length, System.Net.Sockets.SocketFlags.None) If resSize = 0 Then Exit While End If Dim xml3 As String = prev & Encoding.UTF8.GetString(resBytes, 0, resSize) 'Console.WriteLine(“○データ受信(” + resSize.ToString() + “byte)”); 'Console.WriteLine(xml); 'Console.WriteLine(); 'XML解析 '情報情報の形で受信する 'は捨て、情報から情報を取り出す xml3 = xml3.Replace(ControlChars.NullChar, ControlChars.Lf) Dim lines As String() = xml3.Split(ControlChars.Lf) For Each line As String In lines Console.WriteLine(line) '受信XML表示(1行) If line <> "" AndAlso Not line.EndsWith(">") Then 'MAX_RECEIVE_SIZEいっぱいに受信した場合等 'XMLが閉じていない場合は次回Receive時結合する prev = line Exit For End If 'TextBox1.Text = xml3 If line.StartsWith("<thread") Then 'チケット、コメントサーバー時刻取得 'を取得 Dim ticket As String = "" Dim server_time As String = "" Dim xdoc5 As New XmlDocument() xdoc5.LoadXml(line) Dim root5 As XmlElement = xdoc5.DocumentElement For Each attrib As XmlAttribute In root5.Attributes If attrib.Name = "ticket" Then ticket = attrib.Value End If If attrib.Name = "server_time" Then server_time = attrib.Value End If Next If (ticket = "") OrElse (server_time = "") Then End If 'コメント処理開始時刻 Dim m_DateTimeStart = DateTime.Now 'vpos(放送経過時間[sec]*100)を算出 'コメントサーバ開始時間 Dim serverTimeSpan As Int64 = Int64.Parse(server_time) - Int64.Parse(m_base_time) 'コメント投稿時間(1コメゲッターなのでここでは0secですね) Dim localTimeSpan As Int64 = GetUnixTime(DateTime.Now) - GetUnixTime(m_DateTimeStart) Dim vpos As String = ((serverTimeSpan + localTimeSpan) * 100).ToString() 'postkeyを取得 If Not GetPostKey() Then End If 'コメント投稿(1コメ) '注)最後に”を挿入する param = [String].Format("<chat thread=""{0}"" ticket=""{1}"" vpos=""{2}"" postkey=""{3}"" mail=""184"" user_id=""{4}"" premium=""1"">わくおつ~</chat>" & vbNullChar, m_ComSrvThread, ticket, vpos, m_postkey, m_userid) data = Encoding.UTF8.GetBytes(param) sock.Send(data, data.Length, System.Net.Sockets.SocketFlags.None) Console.WriteLine("○投稿します") Console.WriteLine(param) End If If line.StartsWith("<chat_result") Then 'コメント投稿結果 'ここを取り出す Console.WriteLine("(投稿応答)") Console.WriteLine() End If '(“<chat ")にするとエラーが出るため変更 If line.StartsWith("chat ") Then 'コメント取得 'ここを取り出す Dim xdoc6 As New XmlDocument() xdoc6.LoadXml(line) Console.WriteLine(xdoc6.InnerText) End If Next End While End Sub 'Unixタイムに変換 Function GetUnixTime(targetTime As DateTime) As Int64 Dim elapsedTime As TimeSpan = targetTime.ToUniversalTime() - New DateTime(1970, 1, 1, 0, 0, 0, 0) GetUnixTime = elapsedTime.TotalSeconds() End Function Function GetPostKey() As Boolean Try '注) 'block_no = 最新コメント番号(レス番) ÷ 100 'らしい。1コメゲッタなので0にしてる。 Dim url As String = "h" & "ttp://live.nicovideo.jp/api/getpostkey?thread=" + m_ComSrvThread + "&block_no=0" 'h" & "ttp GET リクエストの作成 Dim req As HttpWebRequest = WebRequest.Create(url) req.CookieContainer = m_cc '取得済みのクッキーコンテナ Dim res As WebResponse = req.GetResponse() Dim resStream As Stream = res.GetResponseStream() Dim sr As StreamReader = New StreamReader(resStream, Encoding.UTF8) Dim text As String = sr.ReadToEnd() sr.Close() resStream.Close() '応答はプレーンテキストで 'postkey=ergerwhg54hy4wfwegrghg 'のように返ってくる m_postkey = text.Substring(8, text.Length - 8) Catch e As Exception Console.WriteLine("Message :n" + e.Message) Console.WriteLine("Type :n" + e.GetType().FullName) Console.WriteLine("StackTrace :n" + e.StackTrace.ToString()) Return False End Try Return True End Function End Module気になった注意事項として、GetPostKey()関数が1コメゲッター用のため、コメビュ用途として使用するには以下改造が必要です。
・GetPostKey()で本来はblock_noを算出する必要があります。
コメ番100コメごとにm_postkeyを更新する必要ありです。
threadタグのlast_resやchatタグのnoの値からblock_noの値を決めるのがよいかと思います。
(1コメゲッターは上記実装をサボって、block_noを常に0にしてます。総コメ数が100コメに満たない放送ではそのままのコードでいけます。)
ロフトくんさん、返信有難う御座います。
おかげで様で上手く出来ました。
原因はblock_noでしたね。
参加コミュ・非参加コミュではなく、コメが100オーバーが、そうではないか・・・
それで、パラメータエラーでstatus=”4″が返ってくる。
有難うございます。
2週間後ぐらいは、大まかな枠組みは完成するんじゃないかと思います。
これを作り始めて、VBにはこんな機能があったのか・・・など、VB自体の勉強にもかなりなっています。
C#もそうだと思いますが、同じ処理をするのにもいろいろあって、奥が深いですね・
>佐々木さん
こんにちは。
block_noでしたか、判明してよかったです。
私もツール作成しているときに新しい発見があるといろいろと勉強になります。
こんにちは。
上手くいくはずが、上手くいかず、少し困っています。
もし、よろしければアドバイスよろしいでしょうか・・・
m_postkeyが取得できず、コメ投稿が出来ずにいます。
block_no=”0″にするとm_postkeyは取得できます。
下のコードで出来るはずなのですが、何故できないのかが分からない・・・
‘commentNumberにはlast_resからとったLastのレス番が入っています。
Dim commentNoCC As Double
Function GetPostKey() As Boolean
Try
‘commentNumberが1285の場合は、12.85になります。
commentNoCC = commentNumber / 100
commentNoCC = commentNoCC.ToString()
Dim url As String = “h” & “ttp://live.nicovideo.jp/api/getpostkey?thread=” + m_ComSrvThread + “&block_no=” & commentNoCC
.
.
.
URLまでは上手く行っているのですが・・・
http://live.nicovideo.jp/api/getpostkey?thread=1149930xxx&block_no=12.85
これだと、m_postkeyにはなにも入らないという。
当然、前の段階のtextにも入っていません。
なぜだか分かりますでしょうか?
何度もすみませんm(_ _)m
>佐々木さん
こんにちは。
整数型にすると取得できるようです。
Function GetPostKey(count As String) As Boolean Try Dim block_no As UInt64 = UInt64.Parse(count) / 100 Dim url As String = [String].Format("h" & "ttp://live.nicovideo.jp/api/getpostkey?thread=" + m_ComSrvThread + "&block_no={0}", block_no)ロフトくんさん、有難うございます。
上手くいきました。
UInt64というものを今まで使ったことがなく、また勉強になりました。
こんにちは。私は、VB.NET を独学で学んでいる者です。現在、VB.NETでニコ生コメントビューアーを作ろうと考えています。大変参考・勉強になりました。
>>佐々木さん
VB2010への移植が完了したら、公開する予定はありますでしょうか? もしよろしければ、VB2010 版として、どこかで公開して頂けませんでしょうか?
VB2010でのソースコードを読解して勉強したいと思っています。勝手なお願いで申し訳ありません。よろしくお願いします。
>島田さん
こんにちは。
私はもっぱらC#を使っておりますが、歴史が浅い分、
世の中にはVB使いの方の方が多いかもですね。
C#のソースをVBに変換するツールもあるようなので、
変換精度が良ければ^^、役立つかもしれません。。
>島田さん
ロフトくんさんさえ良ければ、公開したいと思っています。
メインコードのほとんどが、ロフトくんさんのコードなので・・・^^;
簡単な部分はすでに完成しています。
あとは、DataGridViiewに表示させるのに、現在はAddで登録していっていますが、DataTableに一旦格納してから入れるようにしたいのと、コメントの投稿が何故か不安定なので、そこの修正です。
m_postkeyの問題なのかどうか・・・
それともコメのスピードが早い場合、投稿で指定した時間が遅くなり投稿できないのか・・・
まだ原因が掴めていない状態です。
最初は書き込みできて、postkeyが変わると書き込み出来なくなるって何故でしょう・・・
うーん・・・
アンコちゃんでも同じ現象が起こっていますね。
http://d.hatena.ne.jp/mikurun/20120219/1329614863
>佐々木さん
こんにちは、公開して頂いてよいですよ。
VB関連情報の充実に役立てて頂ければ幸いです。
postkeyは100コメごとに変わるものだった気がしますが、
不具合があるのかな?時間のあるときに試してみます。
こんにちは。
なんとなく原因が分かって来ました。
最初から書き込みが出来ない
vpos=”1176600″ postkey=”O-H2LgY0Jb768f3bHznwGhtWdII”
しかしvposが12~になったら書き込みできるようになった
vpos=”1228200″ postkey=”O-H2LgY0Jb768f3bHznwGhtWdII”
取得を/10にしたり、無変更にしたりといろいろ試してみたのですが、出来ませんでした。
ニコ生が何を変更したのか、探すのも一苦労ですね・・・
>佐々木さん
こんにちは。某大手コメビュのコミュ掲示板で目にしたのですが、postkeyの仕様が変わった可能性があるみたいですね。
ニコ生側でなんらかの仕様変更があった可能性がありますね、、
そのコメビュでは暫定対策として、投稿に失敗したら再度postkeyを取得しなおしているようです。
ロフトくんさん、返信有難う御座います。
そっちの掲示板は見ていませんでした。
真っ先に見なければいけない掲示板でしたね・・・
>そのコメビュでは暫定対策として、投稿に失敗したら再度postkeyを取得しなおしているようです。
なるほど、postkeyを再取得で投稿ですか、、
さっそく試してみます。
有難うございます。
承認制に変更?
Private Sub GetCommnet()
・
・
・
‘コメントリクエストメッセージを送信
‘注)最後に”を挿入しないとレスポンスは返ってこない
Dim param As String = [String].Format(“” & vbNullChar, m_ComSrvThread)
Dim data As Byte() = Encoding.UTF8.GetBytes(param)
sock.Send(data, data.Length, System.Net.Sockets.SocketFlags.None)
‘受信する
Const MAX_RECEIVE_SIZE As Integer = 1024
Dim prev As String = “”
While True
Dim resBytes As Byte() = New Byte(MAX_RECEIVE_SIZE – 1) {}
Dim resSize As Integer = sock.Receive(resBytes, resBytes.Length, System.Net.Sockets.SocketFlags.None)
If resSize = 0 Then
Exit While
End If
Dim xml3 As String = prev & Encoding.UTF8.GetString(resBytes, 0, resSize)
‘Console.WriteLine(“○データ受信(” + resSize.ToString() + “byte)”);
‘Console.WriteLine(xml);
‘Console.WriteLine();
‘XML解析
‘情報情報の形で受信する
‘は捨て、情報から情報を取り出す
xml3 = xml3.Replace(ControlChars.NullChar, ControlChars.Lf)
Dim lines As String() = xml3.Split(ControlChars.Lf)
For Each line As String In lines
Debug.WriteLine(line)
MsgBox(line)
‘受信XML表示(1行)
If line “” AndAlso Not line.EndsWith(“>”) Then
‘MAX_RECEIVE_SIZEいっぱいに受信した場合等
‘XMLが閉じていない場合は次回Receive時結合する
prev = line
Exit For
End If
‘TextBox1.Text = xml3
If line.StartsWith(“<thread") Then
'チケット、コメントサーバー時刻取得
'を取得
Dim ticket As String = ""
Dim server_time As String = ""
Dim xdoc5 As New XmlDocument()
xdoc5.LoadXml(line)
Dim root5 As XmlElement = xdoc5.DocumentElement
For Each attrib As XmlAttribute In root5.Attributes
If attrib.Name = "ticket" Then
ticket = attrib.Value
End If
If attrib.Name = "server_time" Then
server_time = attrib.Value
End If
Next
If (ticket = "") OrElse (server_time = "") Then
End If
'コメント処理開始時刻
Dim m_DateTimeStart = DateTime.Now
'vpos(放送経過時間[sec]*100)を算出
'コメントサーバ開始時間
Dim serverTimeSpan As Int64 = Int64.Parse(server_time) – Int64.Parse(m_base_time)
'コメント投稿時間(1コメゲッターなのでここでは0secですね)
Dim localTimeSpan As Int64 = GetUnixTime(DateTime.Now) – GetUnixTime(m_DateTimeStart)
Dim vpos As String = ((serverTimeSpan + localTimeSpan) * 100).ToString()
'postkeyを取得
GetPostKey(comentnumber)
param = [String].Format("” & comtext & “” & vbNullChar,
m_ComSrvThread, ticket, vpos, m_postkey, m_userid)
End If
data = Encoding.UTF8.GetBytes(param)
sock.Send(data, data.Length, System.Net.Sockets.SocketFlags.None)
Debug.WriteLine(“○投稿します”)
Debug.WriteLine(param)
End If
If line.StartsWith(“<chat_result") Then
'コメント投稿結果
'ここを取り出す
Debug.WriteLine("(投稿応答)")
Console.WriteLine()
End If
If line.StartsWith("<chat ") Then
'コメント取得
'ここを取り出す
Dim xdoc6 As New XmlDocument()
xdoc6.LoadXml(line)
Debug.WriteLine(xdoc6.InnerText)
End If
Exit Sub
Next
End While
End Sub
それで、帰ってきた status=”4″ で判断しようと思っているのですが、コードをいじってたら返ってこなくなってしまいました・・・
lineを確認しているのですが、ひとつしか返ってきません。
わくおつ
暇な時にでもコードを見て頂けたら有難いです・・・・
Private Sub GetCommnet()
と
Private Shared Function GetCommnet() As Boolean
の違いも関係あるでしょうか?
↑コメントの順番が逆ですね・・・
こんにちは。
原因解決しました。
投稿出来てて、途中から投稿できなくなった場合は、postkeyが変更されているので、前のpostkeyに戻すと投稿できる。
そしてまた投稿できなくなったら、postkeyを最新のpostkeyに戻す。
この繰り返しで上手くいきます。
>佐々木さん
こんにちは、情報有難う御座います。
投稿に失敗した場合はpostkeyを変更しないといけないのですね。
何かしら理由があっての仕様だと思いますが、、、、、謎仕様ですね^^
ロフトくんさん、こんにちは。
なんとか無事に完成しました。
いろいろと分からないことを助けて頂き有難うございました。
仕様書はまだ出来ていませんが・・・
ソフトへのリンクはこちらに貼ってもいいでしょうか?
>佐々木さん
こんにちは、完成おめでとうございます。
貼って頂いてよいですよ。
こんにちは。
こちらが完成したソフトの置き場所になります。
http://niconama.jimdo.com/
ところで、CookieGetterSharp.dll を使って、Webbrowserで接続する場合もあると思うのですが、CookieContainerを使って送信する場合、Cookieには何か書式とかあるのでしょうか?
>佐々木さん
こんにちは、サイト見ました、高機能ですね、これは流行る!
Webbrowserクラスで接続すると、(Microsoftだから・・)
IEのクッキーが使われるようなことを聞いたことがあるような気がします。
ロフトくんさんはじめまして。
始めたばかりの初心者ですが、上記のコードを参考にさせて頂き、ニコ生のコメビュ作っています。
カン違いや間違いがあるかもしれませんが、コメントを頂けると幸いです。
上記のコードにGUIをつけ、Threadで別スレッドでのコメントの取得をして、それをDataGridViewに表示させているのですが、エラーで止まってしまう事があります。
具体的には弾幕など、1024近い文字数を受信する時に起きるようです。
エラーが起きているラインを見ると上記プログラムでいうと、
if (line.StartsWith(“<chat"))
{
//ここを取り出す
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(line);
のxdoc.loadXml(line)にて
「予期しないファイルの終わりが検出されました。次の要素が閉じられていません」と出ていました。
if (line != “” && !line.EndsWith(“>”))
{
//MAX_RECEIVE_SIZEいっぱいに受信した場合等
//XMLが閉じていない場合は次回Receive時結合する
辺りがまずいのだろうと思い、
if (line != “” && !line.EndsWith(“chat>”))にしてみたり、MAX_RECEIVE_SIZEを増やしてみたりしたのですが改善されません。エラーがでたlineを見てみると、途切れた所で終わった文字列と次に受信した文字列が結合されているようです。
参考に、コードを付けておきます。
http://ideone.com/cfjd4
お時間があれば、お知恵を貸して頂けると幸いです。宜しくお願いします。長文失礼しました。
>さぎりさん
こんにちは。
気づいた点として、ブログ本文に掲載しているコードの
GetCommnet()の修正箇所を2箇所お知らせします。
以下★の処理の追加が必要です。
一度prevを使ったら空文字でクリアしておかないと、次回古いprevを結合してしまいます。
string xml = prev + Encoding.UTF8.GetString(resBytes, 0, resSize); prev = ""; //★prevを空文字でクリアしておく以下は文字列が中途半端なところで切れているならばという条件ですが、
条件が甘いので強化が必要です。
> でなければ(甘い条件)を、
chatの閉じタグ( /chat> )か、threadの閉じタグ( /> )でなければ(強化した条件)
に変更しています。(他にタグの種類がありましたら追加して下さい。)
甘い条件だと、<chat ~~~>←ちょうどここまでを受信した際に閉じタグと誤判定します。
//if (line != "" && !line.EndsWith(">")) //甘い if ((line != "") && !line.EndsWith("/chat>") && !line.EndsWith("/>"))同様の修正をGetProgramInfo()にも必要そうです。
>ロフトくんさん
こんばんは
早いレスありがとうございます。
なるほど、途切れる位置によって色々なパターンが考えられるんですね・・・
上のコードを見よう見まねで書いているのでわかっていない事が多そうです。色々、パターンを考えながらやってみます。
ありがとうございました><
こんにちは。
またまた助言をお願いしたく書く込しました。
BSPの権限を与えられた人用の投稿方法とか分かりますか?
運営コメでBSPの投稿はできてのですが、生主じゃなくBPS取得者のリスナーでの投稿方法が分からなくて・・・
http://watch.live.nicovideo.jp/api/broadcast/
に、tokenと /press show 色 コメント BPS取得者名
でいいんですかね~
自分がBSP取得者ではないので、テストも出来ずなかなか上手く出来ませんね
検索してみても、運営コメの方法は書いてても、一般のBSPコメ投稿は誰もまだ書いていなく見つからないんですよね。
>佐々木さん
こんにちは。
以下は、ニコわんこをBSP対応する際に調べた内容です。
(調べ方としては、BSP権限のあるアカウントでBSP投稿した際の通信をwiresharkで覗き見です。)
BSPコメを投稿できる環境がないとテストがむずいですね。
・APIのURL
http://live.nicovideo.jp/api/presscast
・POSTパラメタ
v=lv○○○○○
body=BSPコメント本文
color=BSPコメント色(0=niconicowhite, 1=red,,,)
name=BSPコメント投稿者名
mode=”json”
token=トークン(presscast_token)
※presscast_tokenは、BSP権限者ならば、放送ページHTMLに
書いてあるのでそれを使っています。
・レスポンスパラメタ
POSTの応答として以下のようなjsonデータが返ってきます。
statusでBSP投稿OK/NGが判定できます。
{
“status”:”ok”,
“code”:””,
“description”:”コメント”
}
ロフトくんxさん、いつも有難うございます。
環境がなく、なかなかテストすることが難しいのですが、BSPの人に頼んでテストしながらやって行きたいと思います。
有難うございます。
助かりました。
初めましてここのソースからニコ生座席取得ツールを作ろうと考えているものです
自分が考える方法としては自分の指定したコミュorユーザーの放送が始まると1コメを送信せずに座席のみを取得して座席番号を表示させてコメビュ機能を省こうと思っています
そこで質問ですがこのソースコードのコメビュ機能となる部分はどこに当たりますか?
まだまだ初心者なので勉強不足なのは承知ですがよろしければ教えて下さい
コレで大手のすぐに満員になるような放送にも入れるようにと思って作るつもりです
>ゆたんぽさん
こんにちは。
コメント処理はGetCommnet()でやっています。
コメント鯖に接続、1コメを送信、受信コメを表示(コメビュ機能)を実施しています。
ちなみに座席取得はGetPlayerStatus()でやっています。
座席情報としてはroom_label、room_seetnoの値を見てみてください。
(サンプルコードのuser_idの値の取り出し方が参考になるかと思います。)
はじめまして。
ニコわんこおもしろそうだったので、試してみました。
参加コミュ情報が取得できずに困っております。ログには「クッキーの共有ができませんでした。」使用しているブラウザはgooglechromeなのですが、どこが問題なのでしょうか?
>>初心者さん
こんにちは。
※こちらのブログ記事は、アラートやコメビュを自作したい方向けの
プログラミング方法の記事になります。
ニコわんこのサポートはTwitter( https://twitter.com/loftkun )、または
コミュ掲示板 http://com.nicovideo.jp/bbs/co247262 で行なっていますので
今後は、そちらに書き込んで下さい。
確認事項としては、
・GoogleChromeでニコニコにログイン済みであること。
・ニコわんこの、一般設定タブの、クッキー共有元ブラウザに
GoogleChromeを選択していること。
です。
上記を実施しているのにクッキーの共有ができない場合は、
・GoogleChoromeのクッキーを削除して再度試す
・クッキー共有チェックツールが配布されているので判定してみる
http://c-loft.com/nico/intro_NwhoisLoginSystem/
の方法があります(詳細はコミュ掲示板にて)
こちらの記事を参考に、CookieGetterSharpを使ってやってみたのですが、クッキーの取得でエラーが出てしまい、どう変えればいいのかわからないので、投稿させていただきました。
[クッキー取得処理の1行目]
ICookieGetter cookieGetter = CookieGetter.CreateInstance(BrowserType.Firefox4);
上記の”CreateInstance”の部分なのですが、エラーの内容が「古い形式です」となっているのですが、変更になったとかで、新しい形式とかあるのでしょうか?
もしかしたら、PCの環境なのかとも思ったのですが、全くわからない状態で・・。
一応、記すとwin8でVSE2012(C#)/.NET FW4.5です。
別途、.NET FW4を入れるべきなのでしょうか?
もし、おわかりでしたらご教授お願いします。
>STさん
こんにちは。
CookieGetterSharpDistribution20130703を見てみました。
CookieGetter.CreateInstance()メソッドは[Obsolete]属性がついており、
今後サポートされない(orもうサポートされていない)ようです。
開発者さんへ.txtを見ると、
CreateInstances()メソッドが用意されておりこちらを使うことが推奨されているようです。
ICookieGetter[] cookieGetters = CookieGetter.CreateInstances(true); foreach(ICookieGetter cookieGetter in cookieGetters){ if (cookieGetter.Status.BrowserType == BrowserType.Firefox) { //ブログ記事掲載の方法 //System.Net.CookieCollection collection = cookieGetter.GetCookieCollection(new Uri("h"+"ttp://live.nicovideo.jp/")); //System.Net.Cookie user_session = collection["user_session"]; //開発者さんへ.txtの方法 System.Net.Cookie cookie = cookieGetter.GetCookie(new Uri("h"+"ttp://live.nicovideo.jp/"), "user_session"); } }なるほど、試してみます。ありがとうございます。
>開発者さんへ.txtを見ると
そういう意味だったのですね。
見ることは見たのですが、意味がわからず見逃してました。
何かありましたら、また、お世話になるかもしれませんが、その時はよろしくお願いします。