ボトムズ日記

プログラミングのことをつぶやく日記です。

32歳で電気通信大学大学院入試 博士前期課程に合格した

先日、電気通信大学大学院入試 博士前期課程に合格しました。本日、合格証が自宅に届きました。今の僕に関係するすべての人に感謝します。ありがとうございました。28歳の時に電気通信大学の夜間課程(通称K課程)に入学して早3年、大変なことが多かったですが、おかげさまで何とか続いています。指導教員と相談した結果、働きながら修士を目指すことになりました。

大学院入試について

K課程の生徒は、外部推薦入試という扱いになり、内部生とは扱いが異なりました。(この外部と内部の違いは正直分かりませんが、噂によると内部はほぼ100%合格するらしく、落ちるとしたら外部みたいです。データはないので噂程度です。)K課程学生課に対して推薦の可否を伺い、GPAが基準以上を満たせば推薦入試の資格を得ることができました。入試書類として特別なものは『志望動機書』、『指導教員の推薦状』が必要でした。僕の場合は、以前所属していた大学の単位の一部を電通大の認定単位として申請しています。そのため前の大学の成績証明書も必要でした。面接について聞かれたことは以下のとおりです。これは僕が社会人だから聞かれたことがほとんどです。多くの受験生には当てはまらないものだと思います。

  • 今までのキャリアについて
  • 志望動機(知りたい方は本人に直接聞いてください。)
  • 社会人との両立について
  • 大学で興味を持った科目は何か
  • 自分の研究が社会に対してどのように役立ちそうか

電気通信大学大学院入試 推薦入試 外部受験 体験談 博士前期課程 - もどくんちゃんねる ガジェット部 を参考にして面接に臨んだのですが、聞かれた内容はかなり異なりました。

残りの学部の生活と大学院に入学した後について

残りの学部の生活は、とにかく今の研究を卒論にして書き上げることに集中します。また取得単位がいくつか残っているので、研究と並行してそれらの単位を取得をしなければなりません。大学院に入学した後は、必要取得単位数が30単位あります。僕はフレックス制度のある会社に勤めておりコアタイムが存在するので、コアタイム以外でなんとか取れるようにします。卒業年数は今のところ2年を考えていますが、もしかしたら2年より多く在籍する可能性もあります。働きながら電通大の博士前期課程をこなすことはイレギュラーであり、参考にならない部分も多いかと思います。

あっさりと書きましたが、以上が僕の現状でした。

Twitterのメンションに反応するRubyスクリプトを書いた

この記事は下記の記事の続きです。

leokun0210.hatenablog.com

Twitter botを作成していましたが、メンションに反応するために必要なUser Streams APIが2018年に終わっていました。したがってTwitter APIで実現させなければなりません。そこで今回は自前でメンションに反応するスクリプトを作成しました。このスクリプトは、cronで1分ごとに実行しています。前回に引き続きtwitter gemを使用します。

Twitterの“User Streams API”が完全終了 ~それでもリアルタイム更新を楽しむ方法 - やじうまの杜 - 窓の杜

require "twitter"
require "redis"

class Card
#省略
end

def get_image(client)
#省略
end

client = Twitter::REST::Client.new do |config|
  config.consumer_key = ""
  config.consumer_secret = ""
  config.access_token = ""
  config.access_token_secret = ""
end

redis = Redis.new

client.mentions_timeline.each do |tweet|
  tweet_id = tweet.id
  unless redis.get(tweet_id)
    pos = tweet.text =~ /ドロー|draw|Draw/
    if pos
      retry_count = 0
      card = nil

      while true
        card = get_image(client)
        if card || retry_count > 5
          break
        end
        retry_count += 1
      end

      if card
        client.update_with_media("@#{tweet.user.screen_name} あなたが引いたカードは『#{card.name}", card.img_uri, in_reply_to_status: tweet)
      end
    end
  end
  redis.set(tweet_id, tweet.user.screen_name)
end

メンションされたツイートのタイムラインを取得します

Twitter::REST::Client#mentions_timeline を呼び出して、メンションされたツイートのタイムラインを取得します。optionで取得件数を制限できるようですが、今回はしませんでした。

client.mentions_timeline.each do |tweet|

Redisで過去に返したメンションを記録します

下記の部分はRedisを用いて、過去にメンションされて返信済みのツイートか判定しています。DBを構築するほどではなかったので、Redisで手軽にできないかどうか模索してい見ました。unless redis.get(tweet_id)で、過去に返信したツイートを取得できなかった場合は、返信を行った後にredis.set(tweet_id, tweet.user.screen_name)でRedisのキーにツイートIDを記録します。Valueはなんでもよいので適当にしています。

redis = Redis.new
# 省略
  unless redis.get(tweet_id)
# 省略
  redis.set(tweet_id, tweet.user.screen_name)

正規表現で返信するワードを制限します

Twitter::Tweet#textでツイートの内容を取得できます。すべてのメンションされたツイートに反応するのではなく、特定のワードのみに反応したいので、正規表現を利用しています。ここでは「ドロー」「draw」「Draw」の3つの単語に反応するようにします。

    pos = tweet.text =~ /ドロー|draw|Draw/
    if pos
#省略
    end

メンション元のユーザIDを取得します

tweet.user.screen_nameは、メンション元のユーザIDです。したがって、このユーザIDにメンションします。

        client.update_with_media("@#{tweet.user.screen_name} あなたが引いたカードは『#{card.name}", card.img_uri, in_reply_to_status: tweet)

これぐらいの規模であればRDBではなく、Redis(KVS)で十分だと思いましたが、予想通りでした。特にスクリプトを実行するうえで不便はしてません。またこれぐらいの軽い処理を行うときはKVSを用いるかもしれません。

遊戯王OCGのカードを一枚ランダムにツイートさせる

このようにランダムにカードを画像付きで一枚つぶやくTwitter botRubyで作成します。コードは以下です。Twitterの投稿部分については特に説明を記述しません。このRubyスクリプトをcronで12:00 JSTに実行しています。

require "twitter"
require "net/http"
require "open-uri"
require "nokogiri"

def post(client)
  begin
    uri = URI.parse("https://db.ygoprodeck.com/api/v7/randomcard.php")

    https = Net::HTTP.new(uri.host, uri.port)
    https.use_ssl = true
    res = https.start {
      https.get(uri.request_uri)
    }
    card = JSON.parse res.body
    uri = URI.parse(card["card_images"][0]["image_url"])
    img = uri.open

    name = card["name"].gsub(" ", "_")
    card_uri = "https://yugioh.fandom.com/wiki/#{name}"
    html = URI.open(card_uri).read

    # 取得したhtmlをNokogiriでパースする
    doc = Nokogiri::HTML.parse(html)
    name_ja = doc.xpath("//th[contains(., '(base)')]")[0].parent.children[1].children[1].text

    client.update_with_media("今日の最強カードは『#{name_ja}", img)
  rescue
    false
  end
end

client = Twitter::REST::Client.new do |config|
  config.consumer_key = ""
  config.consumer_secret = ""
  config.access_token = ""
  config.access_token_secret = ""
end

while true
  break if post(client)
end

カード画像を取得する

GET https://db.ygoprodeck.com/api/v7/randomcard.php はランダムでカードを一枚返却してくれるJSON APIです。Net::HTTPを使用してリクエストして、返却されたJSONをparseしています。["card_images"][0]["image_url"]にカード画像のuriがあるので、それをopenします。

    uri = URI.parse("https://db.ygoprodeck.com/api/v7/randomcard.php")

    https = Net::HTTP.new(uri.host, uri.port)
    https.use_ssl = true
    res = https.start {
      https.get(uri.request_uri)
    }
    card = JSON.parse res.body
    uri = URI.parse(card["card_images"][0]["image_url"])
    img = uri.open

カード名の日本語名を取得する

GET https://db.ygoprodeck.com/api/v7/randomcard.phpで取得するカード名は英語です。そのため下記のコードが、カード名を日本語に翻訳します。まずカード名のスペースを_(アンダースコア)に変換します。https://yugioh.fandom.com/wiki/[変換後の英語カード名]で日本語のカード名を取得することができます。カード情報のHTMLを取得した後にNokogiriを用いてパースします。カードの日本語名は<th>タグのJapanese (base)が値のとき、それのいとこの要素に日本語のカード名を持っています。そのためコードはdoc.xpath("//th[contains(., '(base)')]")[0].parent.children[1].children[1].text のように魔術的な書き方をしています。ここはもっと良い書き方があると思います。興味があれば、該当HPのdomの構造を見てみてください。

    name = card["name"].gsub(" ", "_")
    card_uri = "https://yugioh.fandom.com/wiki/#{name}"
    html = URI.open(card_uri).read

    # 取得したhtmlをNokogiriでパースする
    doc = Nokogiri::HTML.parse(html)
    name_ja = doc.xpath("//th[contains(., '(base)')]")[0].parent.children[1].children[1].text

例外処理

下記のコードでは、Twitterに画像を投稿するpostメソッドがtrueになるまでwhileで繰り返しています。理由はGET https://db.ygoprodeck.com/api/v7/randomcard.phpトークンカードやラッシュデュエルのカードの情報を返却するためです。https://yugioh.fandom.com/wiki/[英語のカード名]は、TCG、OCGのformatのカードのみ対応していると推測されるため、404が返却されて例外が発生します。したがってpostメソッドで例外処理を行います。postメソッドが正常実行できるまで繰り返し実行します。

while true
  break if post(client)
end

今後の開発予定

メンションをしてくれたアカウントにランダムに1枚、カード画像をリプライしたいと考えます。

WSLのUbuntu LTSの初期設定一覧

私は、最近WSLの初期設定を2台分行いました。それときに実行したコマンドを下記に記載します。

zsh

普段使用しているシェルはzshなので、導入します。

sudo apt install zsh
sudo chsh -s /usr/bin/zsh

oh-my-zsh

sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

zsh-syntax-highlighting

cd ~/.oh-my-zsh/custom/plugins
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git

rbenv

aptで入れられるrbenvのバージョンが古いためか、新しいrubyバージョンが入れられません。したがってbuildを行います。

git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src
~/.rbenv/bin/rbenv init
mkdir -p "$(rbenv root)"/plugins
git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build

peco

pecoの新しめのバージョンを動かすと、私が利用しているスクリプトが動かなくなってしまいました。そのため古めのpecoを入れています。

# ホームディレクトリ
cd ~/
# tarファイルをダウンロード
sudo wget "https://github.com/peco/peco/releases/download/v0.5.1/peco_linux_386.tar.gz"
# 解凍
sudo tar xzvf peco_linux_386.tar.gz
cd peco_linux_386
# 実行権限変更
sudo chmod +x peco
# PATHの通っている所にpecoを配置
sudo cp peco /usr/local/bin
# バージョン確認
peco --version

starship

curl -sS https://starship.rs/install.sh | sh

anyframe

mkdir ~/.zsh
cd ~/.zsh
git clone https://github.com/mollifier/anyframe

ghq

sudo add-apt-repository -y ppa:longsleep/golang-backports
sudo apt update -y 
sudo apt install -y golang-go
go install github.com/x-motemen/ghq@latest
# ghqはシェルを改めて立ち上げた

dockerがtmuxで動かない

dokcerがtmuxで動きませんでした。したがって、dcokerディレクトリの実行権限を付与してあげる必要があります。

# https://stackoverflow.com/questions/51766179/inside-tmux-docker-commands-wont-work-without-sudo
$ sudo chown "$USER":"$USER" /home/"$USER"/.docker -R
$ sudo chmod g+rwx "/home/$USER/.docker" -R

ネットワークドライブ(Z:)をwslから見えるようにする

ネットワークドライブ(Z:)をwslから見えるようにするため、mountします。

# /mnt/nasというディレクトリはあらかじめ作成しておく。
# またWIndows側でzドライブにnasを割り当てている。
sudo mount -t drvfs 'z:' /mnt/nas/

ファイルを一か所にまとめるワンライナー

PixivでDLしたjpg,png,gifファイルを一か所にまとめるワンライナー

findコマンドの -name オプションで検索対象のファイルを指定します。nl コマンドでファイルのリストに変換します。awkコマンドで、デリミタ . で分割して拡張子を抜き出し、ファイルをリネームします。(そうしないと同一ファイルがあるので失敗する)awkで組み立てたファイルの移動先をxargsでmvコマンドに渡します。-n オプションは引数の数を表しています。

find ./  -name "*.jpg" -or -name "*.png" -or -name "*.gif" | nl | awk -F'[.]' '{ printf("\".%s.%s\" ./_matome/%02d_.%s\n", $2, $3, $1, $3) }' | xargs -n 2 mv