Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ニコニコ動画のコメントをまとめて投稿するブックマークレット
/*
ニコニコ動画のコメントをまとめて投稿するブックマークレットです。
下記のコードの冒頭のコメント部分を「#1:23 コメント内容」の形式で自由に書き換えてください。
ブラウザのアドレスバーに手入力で javascript: と打ってから、コード全体(8-17行目)を貼り付けて、Enterしてください。
# あらかじめニコニコのコマンド欄に shita などのコマンドを書いておくと、すべてのコメントに適用されます。
# テスト用動画 https://www.nicovideo.jp/watch/sm30996075
*/
(function(){const a=`
#0:00 うp乙
#1:23 wwwww
#1:23.45 コンマ秒単位ずらすwwwww
#60:00.0 時刻表記は 1:00:00 でも 60:00 でも 3600 でもおk
#1:25:25 (´・ω・\`)半角のバッククオート記号(やバックスラッシュ記号)は直前にバックスラッシュ記号を追加挿入してください...
`.trim(),b=6e3;let c=document.querySelector(".CommentCommandInput").value,d=JSON.parse(document.querySelector("#js-initial-watch-data").dataset.apiData),e=d.thread.ids.default,f=d.viewer.id,g=d.viewer.isPremium?"1":"0",h=0,j=0,k="",l=(a,b)=>[{ping:{content:"rs:1"}},{ping:{content:"ps:8"}},{chat:{thread:e,user_id:f,premium:g,mail:c+" 184",vpos:a,content:b,ticket:j,postkey:k}},{ping:{content:"pf:8"}},{ping:{content:"rf:1"}}],m=a=>{let b=a.split(":"),c=6000,d=100;switch(b.length){case 3:return b[0]*360000+b[1]*c+b[2]*d;case 2:return b[0]*c+b[1]*d;case 1:return b[0]*d;}},n=0,o=a.split(/(?=#[0-9:.]+\s)/),p="";if(!confirm(`以下のコメントを${b/1e3}秒ごとに計${o.length*b/1e3}秒かけて投稿します:\n\n${o.join("\n")}`))return;let q=setInterval(function(){if(!o[n]){let a=`${o.length}コメントの送信を完了しました。`;return p.length&&(a+=`以下のコメントは投稿に失敗しました:\n\n${p}`),clearInterval(q),alert(a),void location.reload()}let a=n++,b=o[a].match(/^#([0-9:.]+)\s(.+)$/),c=b[1],d=b[2];1>=b.length||fetch("https://nmsg.nicovideo.jp/api.json/thread?version=20090904&thread={thread}".replace("{thread}",e)).then(a=>a.json()).then(a=>{h=Math.floor(((a[0].thread.last_res||0)+1)/100),j=a[0].thread.ticket}).then(()=>fetch("https://flapi.nicovideo.jp/api/getpostkey?thread={thread}&block_no={block_no}&device=1&version=1&version_sub=6".replace("{thread}",e).replace("{block_no}",h),{credentials:"include"})).then(a=>a.text()).then(a=>{k=a.replace(/^postkey=/,"")}).then(()=>fetch("https://nmsg.nicovideo.jp/api.json/",{method:"POST",body:JSON.stringify(l(m(c),d))})).then(a=>a.json()).then(a=>0===a[2].chat_result.status).then(b=>{b||(p+=`${o[a]}\n`)})},b)
})();
@knoajp

This comment has been minimized.

Copy link
Owner Author

@knoajp knoajp commented Nov 15, 2018

/* こちらは動作紹介用の元コードです */
(function(){
  const COMMENTS = `

#0:00 うp乙
#1:23 wwwww
#1:23.45 コンマ秒単位ずらすwwwww
#60:00.0 時刻表記は 1:00:00 でも 60:00 でも 3600 でもおk
#1:25:25 (´・ω・\`)半角のバッククオート記号(やバックスラッシュ記号)は直前にバックスラッシュ記号を追加挿入してください...

  `.trim();
  const NMSG = 'https://nmsg.nicovideo.jp/api.json/thread?version=20090904&thread={thread}';
  const FLAPI = 'https://flapi.nicovideo.jp/api/getpostkey?thread={thread}&block_no={block_no}&device=1&version=1&version_sub=6';
  const POST = 'https://nmsg.nicovideo.jp/api.json/';
  const INTERVAL = 6000;
  let command = document.querySelector('.CommentCommandInput').value;
  let apiData = JSON.parse(document.querySelector('#js-initial-watch-data').dataset.apiData);
  let thread = apiData.thread.ids.default;
  let user_id = apiData.viewer.id;
  let premium = apiData.viewer.isPremium ? "1" : "0";
  let block_no = 0;
  let ticket = 0;
  let postkey = '';
  let getChat = (vpos, content) => [
    {ping: {content: "rs:1"}},
    {ping: {content: "ps:8"}},
    {chat: {
      thread: thread,
      user_id: user_id,
      premium: premium,
      mail: command + " 184",
      vpos: vpos,
      content: content,
      ticket: ticket,
      postkey: postkey
    }},
    {ping: {content: "pf:8"}},
    {ping: {content: "rf:1"}},
  ];
  let toVpos = (time) => {
    let t = time.split(':'), h = 60*60*100, m = 60*100, s = 100;
    switch(t.length){
      case(3): return t[0]*h + t[1]*m + t[2]*s;
      case(2): return t[0]*m + t[1]*s;
      case(1): return t[0]*s;
    }
  };
  let i = 0, comments = COMMENTS.split(/(?=#[0-9:.]+\s)/), errors = '';
  if(!confirm(`以下のコメントを${INTERVAL/1000}秒ごとに計${comments.length * INTERVAL/1000}秒かけて投稿します:\n\n${comments.join('\n')}`)) return;
  let timer = setInterval(function(){
    if(!comments[i]){
      let message = `${comments.length}コメントの送信を完了しました。`;
      if(errors.length) message += `以下のコメントは投稿に失敗しました:\n\n${errors}`;
      clearInterval(timer);
      alert(message);
      location.reload();
      return;
    }
    let index = i++, c = comments[index].match(/^#([0-9:.]+)\s(.+)$/), time = c[1], content = c[2];
    if(c.length <= 1) return;
    fetch(NMSG.replace('{thread}', thread))
      .then(response => response.json())
      .then(json => {block_no = Math.floor(((json[0].thread.last_res || 0) + 1) / 100); ticket = json[0].thread.ticket;})
      .then(() => fetch(FLAPI.replace('{thread}', thread).replace('{block_no}', block_no), {credentials: 'include'}))
      .then(response => response.text())
      .then(text => {postkey = text.replace(/^postkey=/, '')})
      .then(() => fetch(POST, {method: 'POST', body: JSON.stringify(getChat(toVpos(time), content))}))
      .then(response => response.json())
      .then(json => json[2].chat_result.status === 0)
      .then(success => {if(!success) errors += `${comments[index]}\n`});
  }, INTERVAL);
})();
@knoajp

This comment has been minimized.

Copy link
Owner Author

@knoajp knoajp commented Nov 16, 2018

もともとは、SZBH方式の動画に自分の感想をまとめて投稿したい人向けに作りました。
テレビなどで円盤視聴する人が、再生時刻と照らし合わせながらあらかじめ自分の感想をテキストでまとめておいて、あとで一括して投稿するのを支援するイメージです。
別の場所から希望する動画へのコメントの移植用にもご利用いただいています。

いちおう1投稿あたり6秒なら連投規制には引っかからないようなので、そのように作ってあります。

より高機能なユーザースクリプト版:
https://greasyfork.org/ja/scripts/380484-niconico-batch-commenter
ブックマークレット版に比べて、

  • 一度インストールさえしてしまえば、起動が簡単です。
  • 個別コメントごとにコマンド指定ができます。
  • ` などの特殊文字の処理が不要です。
  • 投稿成功したコメントの先頭に「OK」が付いていくので、投稿の進捗状況がわかります。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment