技術日誌

野球関係のサービスを個人開発しています。

twitter API 有料化対応

twitter APIが有料化されることを受けて、

  • 自動投稿系サービスの移転
  • twitter APIからデータを取得していた部分の改修

を行いました。

自動投稿系サービスの移転

NodeJSとVercel Functions(NodeJS)からtwitter APIを叩いている2つのサービスがあり、 いずれもマストドンへ投稿するように以下の記事を参考にマストドンAPIへと切り替えた。

qiita.com

twitter APIからデータ取得していた部分の廃止

firebaseでtwitterログインを実装しているものの、firebaseのサーバサイドでは

  • twitterのID(通し番号)
  • 現在の表示名

は取得できるが、twitterスクリーンネーム(@以降)が取得できなかったため、 twitter APIを用いてスクリーンネームを別途取得していました。

クライアントサイドだとログイン時にfirebaseから渡されるようなのですが、そちらを修正するのはまた工数が要るので...

qiita.com

データベースにFirebaseのUUIDだけ格納しておけば、コンソールから紐づいているアカウントがわかるので、 多少運用の手間が増えますが致し方ないところです。

キャップ野球情報局更新[2.39.0 → 2.39.2]

チームページ/エイリアス・連合表示

チームページにどのチームの別名(alias)なのか、連合チームなのかどうかという表示をするように修正

成績内訳に対戦チームを表示

成績の試合ごとの内訳で試合日付と試合番号のみを表示していたが、対戦チームを表示するように修正

試合ページのカラムで未使用のものを除去

自作PCの構成まるっと変えました(9年ぶり)

久々に自作PCの構成を一から変えました。 光学ドライブと起動以外のHDDはそのまま引越しです。

旧構成 新構成
CPU Intel Core i7-3770S(3.1GHz/4C/8T) AMD Ryzen5 5600X(3.7GHz/6C/12T)
RAM DDR3 16GB DDR4 32GB
M/B ASUS P8H67-V REV3.0 MSI B450 GAMING PLUS MAX
SSD TOSHIBA 128GB(SATA 6G) WD 500GB(M.2 NVMe)
VGA On Board GeForce GTX 1050
ケース OWL-612 ANTEC P10 flux
Optical PIONEER BDR-209 同左
電源 SilverStone(容量不明) 玄人志向 650W
モニタ 三菱RDT231WM(23.1inch/FHD) DELL S2721Q(27inch/4K)

ベンチマークスコアは30%up。 f:id:ckoshien:20210509232913j:plainf:id:ckoshien:20210509232930j:plain 動画エンコードの速度は同じ設定のままで3倍近くになりました。 9年前でもそこそこスペックにこだわって作ったので結構もちましたが、 GW暇過ぎたので思い切って買い替えました。さすがに起動は速くなりました。

MSI B450 GAMING PLUS MAX ATX マザーボード MB4821

MSI B450 GAMING PLUS MAX ATX マザーボード MB4821

  • 発売日: 2019/08/09
  • メディア: Personal Computers
玄人志向 ビデオカードGEFORCE GTX 1050搭載 GF-GTX1050-2GB/OC/SF

玄人志向 ビデオカードGEFORCE GTX 1050搭載 GF-GTX1050-2GB/OC/SF

  • 発売日: 2016/11/01
  • メディア: Personal Computers
ANTEC P10 FLUX

ANTEC P10 FLUX

  • 発売日: 2021/01/30
  • メディア: Personal Computers

react-planetがsafari12系で動かない問題

react-planet

concept.png 手軽に円形メニュー(Circular Menu)を提供できるライブラリ。

ResizeObserver is not defined

react-planetではリサイズイベントをResizeObseverによって検知しています。

ただし、ResizeObserverはsafari 13.1以降に対応しています。 手元にあるiPhone5Sはsafari12系でした。

react-planet ┗ use-resize-observer   ┗ resize-observer-polyfill

今回、おそらく動いていないのはresize-observer-polyfillのようです。 きちんとメンテされてそうなのはこれ。 resize-observer

ただ、use-resize-observerのソースを書き換えて、 react-planetのdependencyも変えないといけないので一旦調査してエラー回避する方向で終了。

TopMenuコンポーネントの中でreact-planetを使っています。

 {typeof ResizeObserver !== 'undefined' 
          ? 
          <div id="topMenu">
            <TopMenu/>
          </div>
          :<></>
        }

キャップ野球情報局v2.0リリースしました。

キャップ野球情報局」というサイトを作っています。

アップデート履歴

ちまちまとマイナーアップデートを重ねていましたが、 5/17からNextJSへ移植するのと同時に、次のメジャーアップデート(v2.0)を7月リリースを目標に作っています。

次バージョン新機能

twitterアカウントを持っていれば誰でもログインできます。

マイページ

ユーザページ編集

別サービス「みんなのSCORE」のデータに対応する形で選手ページを持っています。

選手へのコメント機能

選手間で「この選手はどういう選手です」という他己紹介をする機能です。

チームページ編集

別サービス「みんなのSCORE」のデータに対応する形でチームページを持っています。 チームの紹介のほか、チームのテーマカラーが設定できます。

イベント登録・編集

キャップ野球には、主なイベントとして

  • 大会
  • リーグ
  • 練習会

がありますが、それらの情報を登録・編集することができます。

次バージョンに採用している技術

  • フロント
    • NextJS
    • ReactJS
    • Netlify
    • TypeScript
  • サーバサイド
    • NodeJS
    • MySQL
    • Docker(-compose)
    • TypeScript
  • ミドルウェアなど
    • slack
    • firebase
    • cloudinary

imgurではなくcloudinaryを採用した理由

運営者ギルドでは画像ストレージとしてimgurを薦められていたのですが、imgurとcloudinary両方を実装して、使いやすさの観点からcloudinaryを採用することにしました。

ログイン

imgurはOAuth認証すればログイン状態でアップロードできるのですが、ブラウザでPINが必要など使い勝手と実装に難がある印象です。

匿名アップロード

比較的実装が簡単ですが、imgurでは匿名アップロードした画像をGUIでは管理できません。

工数的にはcloudinaryのログイン状態アップロード≒imgurの匿名アップロードという印象だったので、GUIで全体管理ができるcloudinaryを選びました。用途としては無料枠で足りると思います。

React/NodeJS/Passportでtwitterログインを実装してみた

こちらの記事をベースにしてReactJS/NodeJSのシステムにtwitterログインを組み込んでみた。 qiita.com

主に異なるのは型指定が緩めななんちゃってtypescriptを使っているところか。

蓋々交換機能

cap-baseball.com f:id:ckoshien:20200414202745j:plain

技術的なこと

passport-config.ts

export default function passportConfig() {
  var TWITTER_CONSUMER_KEY = "*****";
  var TWITTER_CONSUMER_SECRET =   "*******";
  var passport = require("passport"),
    TwitterStrategy = require("passport-twitter").Strategy;

  // Sessionの設定
  passport.serializeUser(function (user, done) {
    done(null, user);
  });
  passport.deserializeUser(function (obj, done) {
    done(null, obj);
  });

  passport.use(
    new TwitterStrategy(
      {
        consumerKey: TWITTER_CONSUMER_KEY,
        consumerSecret: TWITTER_CONSUMER_SECRET,
        callbackURL: "https://********/auth/twitter/callback",
      },
      function (token, tokenSecret, profile, done) {
        passport.session.user = profile;

        // tokenとtoken_secretをセット
        profile.twitter_token = token;
        profile.twitter_token_secret = tokenSecret;
        process.nextTick(function () {
          return done(null, profile);
        });
      }
    )
  );
}

auth.ts

NodeJSでtwitter認証からのコールバックなどを担当するコントローラ。

import * as express from "express";
import * as session from 'express-session'; 
import { Request } from "./interface/express.Request";
const passport = require("passport");

export class Auth{
    public router: express.Router;
    constructor() {
        this.router = express.Router();
        this.router.get("/twitter", passport.authenticate('twitter'));
        this.router.get("/twitter/success", this.success);
        this.router.get("/twitter/callback",
                passport.authenticate('twitter', 
                    { successRedirect: 'https://******/api/v2/auth/twitter/success',
                      failureRedirect: 'https://******/' })
        )}

   
    private success(req:Request,res:express.Response):void{
       if(req.session.passport !== undefined){
        res.json(req.session.passport.user.username);
       }else{
           res.sendStatus(401);
       }
    }
}

express.Requestインターフェースの拡張

import * as Express from 'express';
export interface Request extends Express.Request {
 session:any;
}

app.ts

import { Auth } from './auth';
import * as session from 'express-session';
import passportConfig from './passport-config';
passportConfig();
const passport = require("passport");

app.use(passport.initialize()); 
app.use(passport.session()); 
app.use(
  session({
    secret: '********',
    resave: false,
    saveUninitialized: false,
    cookie:{
      httpOnly: true,
      secure: true,
      maxage: 1000 * 60 * 30
    }
  })
);

ReactでNodeJS/Passportから認証情報を受け取る

credentialsオプションが必要。

  const response = await fetch('/auth/twitter/success',
  {
    method:'GET',
    credentials: "include",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
      "Access-Control-Allow-Credentials": true
    }
  });

認証が終わったタイミングで認証情報を受け取る

認証のためのウインドウを開き、 そのウインドウが閉じられたタイミングで親の画面をリロードする。 リロードの際に認証情報を受け取っている。

onClick={()=>{
                    const authWindow = window.open('/auth/twitter','newTab');
                    var timer = setInterval(function() { 
                        if(authWindow.closed) {
                            clearInterval(timer);
                            window.location.reload();
                        }
                    }, 1000);
                }}

OSSコミットは難しい

FacebookメッセンジャーlikeなUIの実装をしていて、 以下のライブラリを使っているのですが、 github.com このうちChatListというコンポーネントで、どの相手を選択しているかという状態をコンポーネントが持っていなかったため、 このコンポーネントだけスクラッチで作る羽目になりました。

選択した場合は背景色を変えます。

<div
            style={{
              backgroundColor:
                store.getState().target === store.getState().messageSummary[i].target_user
                  ? "#dddddd"
                  : "white",
              margin: 3,
              padding: 10,
              cursor: "pointer",
              height: 72,
              whiteSpace:'nowrap'
            }}

f:id:ckoshien:20200408213155j:plain

Pull Requestとか出せればいいのだけど、割と独自実装なので無理。