about

このブログは私の技術的な知見や活動をまとめるためのものです。

my position

  • Pythonista / Web Engineer / Multimedia Creator

engineering skills

creative skills

links

Qiita/Qiitadonファンミーティングに参加しました

イベント詳細はこちら。
梅雨をふっ飛ばせ!Qiita/Qiitadon ファンミーティング #3 - connpass

Qiita/Qiitadonユーザが集まって、
ピザ食べてお酒飲んでLT聞いたり雑談したりという会です。

雑感

LTテーマが「参加してよかった勉強会、カンファレンス」だったのですが、

という内容のLTで、微妙に聞きたかった話と違うな……?
とは思いましたが面白い話が聞けたので良かったです。
Qiitaの今後のアップデート方針も期待できる内容でした。

小規模で話しやすい雰囲気だったのも良かったですね。
Discord とか Mastodon はまだまだ認知度が低いという話もありました。

インフラ勉強会について

インフラ勉強会のご紹介 - Qiita
Discord 上で毎日勉強会が開催されているようです。
2700人近く参加しているようで、覗いてみるだけでもいかがでしょうか?

GraphQLについて

REST API に代わる新しい API 仕様らしいです。
なんかすごい(小並感)

ざっくりでいいので一度どういうものかを知っておく価値はあると思います。
GraphQL入門 - 使いたくなるGraphQL - Qiita

Qiitadon について

qiitadon.com
Qiita ユーザが集う Mastodon です。
少人数のエンジニアがわいわい技術的な話をしている居心地の良い空間です。

今後のQiita/Qiitadonファンミについて

2ヶ月後を予定しているそうです。
アナウンスを待ちましょう。

「週末Webサービス開発もくもく会」に参加しました

もくもく会について

今回はエイチワークスさん主催のもくもく会に参加してきました。
週末Webサービス開発もくもく会 #7@茅場町 - connpass

フリーランスの方が数名いて、お仕事の相談もしつつ進めていたようですが、
残りの方はひたすら黙々と作業を進める会でした。

会場について

会場はこちら。
コワーキングスペース茅場町 Co-Edo

作業スペースという印象が強いですね。
Wifi や大きいモニターが使用可能で、お茶やコーヒーが無料でした。
2時間で500円、1日で1000円らしいので今後も何かで利用するかも。

作業内容について

自分はQIitaのPythonタグを追っているのですが、
最近初心者の記事がやたら目立って邪魔になってきたので、
それらを弾いた記事一覧を取得するものを作ろうとしています。

今回は普通にPythonタグのついた記事一覧を取得するところまで。
Qiita API の仕様を把握するところで割と時間掛かりましたね。

QiitaのPython記事一覧を取得する - GitHub PullRequests

記事一覧を取得するAPIがあるのでそれを叩くだけかと思いましたが、
何故かご丁寧に記事の本文まで丸ごと取ってくる仕様になっていたので
間に正規化を挟まざるを得ませんでした。めんどくさい。

(地獄絵図) f:id:sweetsoundstory:20180624100208p:plain

test_qiita_client.py を走らせれば
最新の記事10件の情報をjsonで吐いてくれます。

(こんな感じ) f:id:sweetsoundstory:20180624100344p:plain

ストック数も別で取得しなければいけないんだよなぁ(´・ω・`)

PythonでDiscord botを作る 【discord.py解説】

この記事はTUT Advent Calendar 2017の12日目(?)の記事です。

前書き

僕はSlackよりもDiscordが好きです。
アカウントを複数作らなくていいし、通話もできるし、
フレンド機能もあるし、役職設定で楽にグループ管理ができるので。

7500人を超える React のコミュニティが
Slack から Discord に移ったというニュースもあります。

Reactiflux is moving to Discord - React Blog

また Python の Slack コミュニティも Discord への完全移行が決定しています。

ということで、DiscordでもSlackと同じように
botを運用できるんですよって話を書こうと思います。

botアカウントの作成と登録

PythonでDiscord botを作る記事、あるにはあるんですが、
日本語だと参考になるのは以下の2つぐらいしかないです。

Pythonで簡単なDiscord Botの作り方

discordで使える白猫テニス用レート計算botを作ったよ!

botを運用する前にアカウントを作成してサーバーに登録する必要がありますが、
そこまでは上記の記事等に従えばすんなりいけると思うので割愛します。

botプログラムの作成と起動

GitHub - Rapptz/discord.py: An API wrapper for Discord written in Python.

まずはこちらの discord.py を入れます。

$ pip install discord.py

公式のドキュメントに従って python プログラムを作成します。

import discord
import asyncio

client = discord.Client()

@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')

@client.event
async def on_message(message):
    if message.content.startswith('!test'):
        counter = 0
        tmp = await client.send_message(message.channel, 'Calculating messages...')
        async for log in client.logs_from(message.channel, limit=100):
            if log.author == message.author:
                counter += 1

        await client.edit_message(tmp, 'You have {} messages.'.format(counter))
    elif message.content.startswith('!sleep'):
        await asyncio.sleep(5)
        await client.send_message(message.channel, 'Done sleeping')

client.run('token')

token には bot 管理画面から取得できるアクセストークンを設定してください。

作成したプログラムを起動して、
discord上で !test なり !sleep なり発言するとbotが反応します。

ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

というエラーが起動時に発生する場合がありますが、
こちらの記事を参考に

macOS用公式インストーラーのPython 3.6でCERTIFICATE_VERIFY_FAILEDとなる問題

$ /Applications/Python\ 3.6/Install\ Certificates.command

を実行することで解決すると思います。

discord.py を使いこなす

基本的には

@client.event
async def on_message(message):

以下に条件なりを記述して

await client.send_message(message.channel, 'message')

で発言するという形でbotを作っていきますが、
発言に反応するだけのbotでは面白くないですよね?

ということで拡張していくのですが、
ドキュメントが充実していない上に参考になる記事も殆どありません。

英語の解説動画や記事、
公式の discord.py サーバーや Discord API サーバーもありますが、
英語が読めないと話にならないので、
素直にソースコードを読むのが良いと思います。

discord.py/discord/ 配下の
client.py channel.py member.py server.py user.py
あたりを読めば大抵のことは事足りると思います。
特に client.py は接続の起点なので一番大事。

任意のチャンネルで発言させる

例えば "bot_test" というチャンネルで発言させたい場合、

channel_bot_test = [channel for channel in client.get_all_channels() if channel.name == 'bot_test'][0]
await client.send_message(channel_bot_test, '勝手に喋るよ')

というように "bot_test"チャンネルのオブジェクトを取り出して指定します。 但し、この場合チャンネル名が変わった時点でエラーになるので、
"channel.id" で チャンネルID を調べて

[channel for channel in client.get_all_channels() if channel.id == 'XXXXXXXXXXXXXXXXXX'][0]

のように ID で識別するようにしましょう。
正直あまりスマートな方法ではないように思えますが、
これ以外の方法には辿り着けませんでした。

話しかけられたら返事をする

discord での menthon は
@USERNAME という形式で指定して行いますが、
内部的には(message.contentを見るとわかりますが) <@USERID>
という文字列になっています。

なので、client.user.idbot のユーザIDを取得し、
if client.user.id in message.content という条件の時に
message.author.mention を含めて発言をすることで
返信をすることができます。

ユーザー名のリストを取得する

client.get_all_members() で取得できるので、例えば

[member.display_name for member in client.get_all_members()]

で全てのユーザのニックネームのリストを取得できます。
ユーザ名やIDを取得することもできますし、
ボイスチャットに参加しているユーザのみを取得するメソッドもあります。

おわりに

大体以上のことを把握していれば、大抵のことは実現できると思います。

Slack でできることは、Discord でできるので、
みなさんも Discord 使いましょう。

ファイルの種類を表す「MIMEタイプ」

MIMEタイプというのがHTTPレスポンスヘッダにあって、
転送ファイルの種類を示しているらしい。

text/htmlとかimage/pngとかapplication/pdfとか。
unknown/undefinedなファイルの場合は
application/octet-streamになるそうな。

S3からアプリ(バイナリ)をダウンロードする時もこれ。
S3のファイル情報を覗いてみると記載されている。

参考
MIMEタイプ
application/octet-stream【MIMEタイプ】

githubの複数アカウントを使い分けるためにsshからhttpsに切り替えた話

プログラミング用と制作用にgithubアカウントを複数使い分けるようにした。
そこで問題になるのがssh-keyをどう切り替えるか。
結論としては、ssh-keyを切り替えるのは面倒なのでhttps方式を採用した。

https方式にするのは簡単で、git clone時に
Use SSH(Clone with SSH)の"git@github.com:~"ではなく
Use HTTPS(Clone with HTTPS)の"https://github.com/~"を指定する。

git push時の設定は
Caching your GitHub password in Git - User Documentation
を参考に認証情報(キャッシュ)を保存するようにする。
~/.ssh/configのHostを弄ったりする必要はない。

……という単純な話だと思ったのだが、git push時に
"remote: Permission to 制作用ユーザ/repository.git denied to PG用ユーザ."
というエラーに悩まされた。
調べても"Permission denied (publickey)"の対処法ばかり出るし、
.sshを爆破したりgit configのglobalもlocalも弄ったりしてもダメだった。

結局、git cloneするときにユーザ名を指定するやり方
"https://ユーザ名@github.com/username/repository.git"
で解決した。

原因が未だに不明なので教えていただけるとありがたいです。



参考にした記事はこちら

qiita.com

yutainoue008.hatenablog.com

te2u.hatenablog.jp

技科大生活の振り返り

この記事はTUT Advent Calendar 2016 - Adventarの11日目の記事です。


こんにちは。TUT3系B4の丹内です。

昨年と一昨年のTUT Advent Calendarでは以下の記事を執筆しました。
豊橋技科大音楽技術部, たのしいフーリエ解析
豊橋技術科学大学 総合文化部 • エンジニアのためのデザイン入門

今年度は卒業して就職する予定なので、
自身の技科大生活の振り返りと、得られた教訓について書こうかなと思います。
36時間寝続けた話と女装せずに女装コンテストに出た話は諸事情で割愛します。


入学してから先生になるまで

自身が技科大内で何者であったか、ということに関するエピソード。

入学早々にクラス代表に立候補したり、サークルを立ち上げたり、
講義を全履修していたり、8つのサークル掛け持ちしていたり、
その他色々と目立つような行動を重ねていった結果、
名前が独り歩きして知らない人まで自分のことを知っていたり、
「学校で一番有名」ということを言われるようになっていたり。

最終的に何故か周りから「先生」と呼ばれるようになっていました。
先生呼びが定着し、名前で呼ばれることが少なくなった頃、
某氏の陰謀で昨年度の学祭の女装コンテストに出ることになったのですが、
その時送られてきたメールが以下の通り。


どうやら自分はいつの間にか先生になっていたようです。
(本当に先生だと思ってこの文章を送ってきたとのこと。)

因みに今では諸事情で滅多に「先生」と呼ぶ人はいませんが、
過去に呼ばれていた形跡はこちらから確認することができます。
tnni 先生 - Twitter Search


人生初のリーダー経験

ここからが本題。

本学に入るまでリーダーの経験は一切なかったのですが、
気が付けばクラス代表と2つのサークル代表と
合わせて3つのリーダーを並行していました。
自分のことは凡人だと思っていますし、
周りには優れた人や変わった人が多かったので、不思議です。

「音楽技術部」というサークルを立ち上げて、今年度で3年目になります。
(正確には総合文化部の下に音楽技術部門として作りました)
当時を振り返ってみると、入学5日目にして既に着想があり、
2ヶ月経たずに行動に移していて、なかなかの行動力だなぁと思います。
ここ数年衰えが激しいので、今ではもうこういうことできないなぁと。


その辺りの原動力やモチベーションとして、
高専時代にもサークルを立ち上げて活動してた経緯があり、
ある程度ノウハウがあって敷居が低かったというのがあります。
あとは上でも述べたように自分には幸いにも人望には恵まれていたようで、
あらゆる場所で声を掛ける苦労はありましたが、
すぐに人は集まってくれました。
ここ3年で立ち上げに失敗したり消滅していったサークルがいくつかあったので、
このサークルは珍しく上手く存続している運が良い例だと思います。
仲間には感謝しています。

リーダーを務めて得られた知見がいくつかあるので書き出してみます。

・リーダーには独裁者気質が必要
メンバーは勝手に都合よく動いてくれたりはしません。
故にリーダーは指示を出しメンバーを動かし続けなければなりません。
「これでいいのかな」などと優柔不断になっている暇はありません。
リーダーがアクションを起こさなければ活動は停滞し、
メンバーは離れていきます。
企画はリーダーが少なくとも叩き台を出す必要があります。
メンバーが企画を持ってくるという幻想は捨てましょう。
募集も無駄なのでやめましょう。
企画を持ってきてくれる人は募集してなくても持ってきます。
しばらく活動していれば自ずと動いてくれるメンバーは出てきます。
しかし、それまではずっとリーダーは正しく判断し正しい指示を出す
独裁者である必要があります。

・リーダーは傲慢になる
上の独裁者云々とは別の話です。
リーダーの仕事は指示を出すことよりも書類手続きや事務連絡が主になります。地味です。
ある程度メンバーに任せられるものもありますが、
リーダーにしかできない仕事が多いです。
活動よりも仕事をすることが多くなることもあります。
何のためにやってるのか分からなくなることもしばしば。
そんな状況が続くと心が荒んでいきます。
よほど聖人でなければ、
「仕事量が不公平だ」「メンバーは自分勝手」「自分は偉い」
などという思考が渦巻いたりします。
そういう気持ちを抑えて折り合いをつける必要があります。
そういう時はメンバーの何気ない一言が心に沁みるものです。



(途中で力尽きたので後で補完します)




明日はtenntennさんが何か書くそうです。

OSのアップデートと環境設定の見直し

iMacとMBPが未だにYosemiteのままだったので、
双方ともSierraにアップデートしました。

それに伴って環境設定の見直しをした際の備忘録として。
Xcodeの更新があったので見直さざるを得なかった)

Xcode/Command Line Toolsのアップデートの影響で、
gcc/g++のバージョンが不安定で挙動がおかしくなったので、
以下を参考にシンボリックリンクを設定。
nonbiri-tereka.hatenablog.com

ついでにパッケージの更新も以下を参考に。
qiita.com
iganari.hatenablog.com

各種ライブラリのアンインストール(削除)手順と/usr/localについて

MeCabやらCabochaやらの挙動が少しおかしくなったので、
入れ直すためにアンインストールしようと思ったのだが、
アンインストール手順を調べたところ、

1.落としたファイルを解凍したディレクトリでsudo make uninstallしてね!
2.アンインストール方法なんて用意してないよ!

のどちらかだった(しかも落としたファイルはインストール後に削除している)
ので、仕方なく各ファイルを探し当てて消していくことにした。

で、どうやら/usr/local/あたりに色々とあるらしいということが分かった。

dqn.sakusakutto.jp
hateda.hatenadiary.jp
/usr/local ‐ 通信用語の基礎知識

UNIXPOSIX準拠OS(Linux等)で、利用者がインストールしたソフトウェア等を置くディレクトリ。
オペレーティングシステム(OS)によって考え方は異なっているが、Linuxの標準FHSでは、自分でコンパイルしたバイナリは/usr/local以下に置くことになっている。のため、/usr/local以下の使用はホスト管理者に任せられており、一般にOSインストール直後は空になっている。

なるほど。そして以下を実行。

cd /usr/local/lib
rm libcabocha.*
rm libcrfpp.*
rm libmecab.*
rm -dr mecab
cd /usr/local/libexec/
rm -dr mecab

これで完了かな?とりあえずmecabコマンド打ってみよう。

mecab
dyld: Library not loaded: /usr/local/lib/libmecab.2.dylib
  Referenced from: /usr/local/bin/mecab
  Reason: image not found
Trace/BPT trap: 5

なんかまだ生きてるっぽい。PATHあたりに何かヒントあるかも?

echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin

/usr/local/binにまだファイル残ってるっぽい。というわけで以下も実行。

cd /usr/local/bin/
rm mecab
rm mecab-config
rm cabocha
rm crf_*

んでもっかいmecabコマンド打って確認。

mecab
-sh: mecab: command not found

無事にアンインストールできた様子。

この辺参考に入れ直し。utf-8指定しないとダメ。ゼッタイ。
m0t0k1ch1st0ry.com
係り受け解析機「CaboCha」をFreeBSD 10.1にインストールする - Symfoware
github.com

paiza Online Hackathon Vol.8 (POH8) with Python3

ショートヘア

RepeatNum = int(input())
OutputString = input()

for C in range(RepeatNum):
    print(OutputString)

ロングヘア

Integer = int(input())

Answer = "lucky" if Integer % 7 == 0 else "unlucky"
print(Answer)

ポニーテール

CorrectAnserQuant = 0
for n in range(5):
    dn,en = input().split()
    if dn == en:
        CorrectAnserQuant += 1

Answer = "OK" if CorrectAnserQuant >= 3 else "NG"
print(Answer)

ツインテール

TotalLength = int(input())
CurrentStatusNum = int(input())

CharList = ["-" for C in range(TotalLength)]
CurrentIndex = CurrentStatusNum - 1
CharList[CurrentIndex] = "+"
Answer = "".join(CharList)
print(Answer)

おさげ

def Check(MaxLength,MaxSongQuant):
    SongQuant,TotalLength = 0,0
    for n in range(MaxSongQuant):
        SongLength = int(input())
        TotalLength += SongLength
        if TotalLength > MaxLength:
            return(SongQuant)
        SongQuant += 1
    return("OK")

MaxLength = int(input())*60
MaxSongQuant = int(input())

Answer = Check(MaxLength,MaxSongQuant)
print(Answer)

たれ目

RemainSeatsQuant,PeopleQuant = map(int,input().split())

Answer = "OK" if RemainSeatQuant >= PeopleQuant else "NG"
print(Answer)

つり目

p = int(input()) #買物額

Answer = p//100 if p < 1000 else p//100 + 10
print(Answer)

めがね

N = int(input()) #数字の個数
NumSequence = [i for i in map(int,input().split())].sort(reverse=True)

CenterIndex = (N+1)//2 -1
Answer = NumSequence[CenterIndex]
print(Answer)

Cute衣装

#スタッフの人数、パックに入っているアメ玉の数
n,m = map(int,input().split())

Answer = "ok" if m % n == 0 else "ng"
print(Answer)

Sexy衣装

#元の位置から進んだ歩数, 進んだ先から下がった歩数
M,N = map(int,input().split())

CP = M - N #CurrentPosition
Answer = CP if CP > 0 else 0
print(Answer)

制服

CardSequence = ["3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"]
OrderSequence = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
PlayerRankList = [0 for C in range(52)]
PlayerHandList = [OrderSequence[CardSequence.index(Card)] for Card in input().split()]

HighestCardNum,LastPlayerIndex = 0,52
while PlayerHandList.count(0) != 52:
    for Index,CardNum in enumerate(PlayerHandList):
        if LastPlayerIndex == Index:
            HighestCardNum = 0
        elif CardNum > HighestCardNum:
            HighestCardNum = CardNum
            LastPlayerIndex = Index
            PlayerHandList[Index] = 0
            PlayerRankList[Index] = max(PlayerRankList) + 1

for i in rank:
    print(i)

浴衣

N = int(input()) #冷蔵庫からの出し入れの回数

eue2 = 0 #electric utility expense
temp = 0 #temperature
lasttime = 0
for i in range(N):
    ti,si = input().split()
    up = 5 if si == "in" else 3
    tdiff = int(ti) - lasttime
    eue2 += up
    temp += up - tdiff if temp - tdiff >= 0 else up - temp
    lasttime = int(ti)
temp -= 24 - lasttime

answer = 24-eue2 + 2*eue2 if temp <= 0 else 24-eue2 + 2*eue2 - temp
print(answer)

水着

#変更前の文字列の長さ、変更後の文字列の長さ
n,m = map(int,input().split())

s = input() #変更前の文字列
t = input() #変更後の文字列

counter = {}
for c in s:
    if not c in counter:
        counter[c] = 1
    else:
        counter[c] += 1
for c in t:
    if not c in counter:
        counter[c] = -1
    else:
        counter[c] -= 1

answer = sum(-1*i for i in counter.values() if i < 0)
print(answer)

マイク

import math

n = int(input()) #1日にこなせるイベント回数
m = int(input()) #計画されているイベント総回数

answer = math.ceil(m/(n*2))
print(answer)

カチューシャ

import math

#ファンの人数(人) 、色紙1枚の費用(円)
n,p = map(int,input().split())
#ペン1本でサインの書ける色紙枚数(枚)、ペン1本の費用(円)
m,q = map(int,input().split())

answer = math.ceil(n/m)*q + n*p
print(answer)