Atomic Design について考える 〜Buttonコンポーネント編〜
かなり久々の更新。
最近仕事ではc#をやりつつフロントもちょこちょこ触っています。
現在使用しているのはjQueryですが、
いい加減フロント改善したいという機運が社内でも高まってきていて 、
次はVueかReactかみたいな感じになっています。
なので自分も焦ってVue.jsとAtomic Design について勉強しているところです。
その過程で得た自分の理解というか整理を残しておきたくてこの記事を書きます。
開発環境はNuxt.jsなのでその前提で書いてはいますが本題はあくまでAtomic Designについてです。
Buttonコンポーネントについての記事なんて世の中にありふれてるじゃねーかという感じではありますが、
Button一つとっても考えることはたくさんあったので気にせず書いていきます。
ButtonはAtomsかMoleculesか
まずはAtomic DesignにおいてButtonをどこに分類するかです。
先に言ってしまうと自分はどちらでも間違っていないと思っています。
それは何も考えずにどっちに作ってもいいよって意味ではなく、
デザイン定義に沿って決めるべきだと考えているからです。
例えば以下のようなボタンを作成したいとなった場合
1つ目はベーシックなテキストだけ入ったButton
2つ目はIconが入ったButton
前者のButtonであれば迷わずAtomsかなと自分は思います。
ですが後者のButtonだとどうでしょう?
IconはAtomsとして定義しておくべきな気がします。
それが中に含まれているButtonをAtomsとするべきでしょうか?
ButtonをAtomsとした場合、
Buttonコンポーネント内でIconコンポーネントを呼び出すことになってしまいます。
Atomsには最小の単位まで分解したものを定義するはずなのでこれだと違和感を感じます。
ではこの場合はIconコンポーネントをAtoms、
ButtonコンポーネントをMoleculesとして定義すればどうでしょう。
そうすればButtonコンポーネントからIconコンポーネントを呼び出しても違和感は無くなりました。
これで解決したかと思いきや1つ問題が発生しています。
なぜなら後者のButtonをMoleculesにした場合、
前者のButtonはどうすればいいのかとなってしまいます。
前者のButtonをAtomsに定義しても良いですが、
AtomsにもMoleculesにもButtonが存在しているのが少し気持ち悪いです。
かといって前者のButtonもMoleculesに入れても違和感があります...
といった感じで自分も色々悩んだのですが次のような方法で解決しました。
色々言ったがButtonはやっぱりAtomsにする
サブタイの通りButtonはAtomsにしました。
IconもAtomsにしました。
そして1つのButtonコンポーネントでIconが付く場合も付かない場合も表現出来るようにします。
自分は以下のようなButtonコンポーネントを作成しました。
vue.jsのslot機能を使っているので、
Buttonコンポーネントを使用する側の親コンポーネントが 、
slot部分に対してIconコンポーネントを挿入した場合はIcon付きのButton、
挿入しない場合はテキストのみのButtonを表示できるようになっています。
さらにrightIconも定義して右側にIconを挿入することも出来るようになっています。
これでIconコンポーネントはもちろんButton以外でも再利用可能ですし、
Buttonコンポーネントも1つのコンポーネントで様々なButtonを表現出来るようになりました。
もっとButtonコンポーネントについて考える
Icon問題については上の実装で解決できましたが、
Buttonコンポーネントについてはまだまだ考えないといけないことがあります。
なので順番に整理していきましょう。
Buttonの色を変えたい
以下のように先ほど定義したようなベーシックな見た目のButtonだけでなく、
赤色のButtonも定義したいとなったとしたらどうすればいいでしょうか?
新しくRedButtonコンポーネントを作成などしているとコンポーネントが大量に出来てしまいます。
なのでButton.vueを変更して動的に見た目を変更出来るようします。
Buttonコンポーネントを使用する際にpropsのtheme変数に何が指定されているかでclassを動的に変更出来るようになっています。
・themeにwhiteを指定
・themeにredを指定
これでButtonコンポーネントがさらに汎用的に使えるようになってきました。
ですがこれではまだ問題があります。
もしRed Buttonを使っていたけど色を変更して、
全てBlue Buttonに変更したいとなった場合はどうすればいいでしょうか?
Buttonコンポーネント内にBlue Buttonの定義を作成して、
プロジェクト内のthemeにredを指定している箇所を全てリファクタする!
気が遠くなりそうです...
これは色に対して依存をしているのが問題だと思うので、
自分はボタンの持つ役割に対して依存したclass名を使うようにしています。
デザインを定義する際にButtonのスタイルだけでなく、
それぞれのスタイルのButtonが持つ役割も定義されているのではないかと思います。
その役割に対してclass名を依存させておけばcssの変更だけで済むようになるのではないでしょうか。
ということでclass名を変更します。
今回はsubmitとcancelという2つを設定してみました。
Buttonを無効化させたい
次はButtonを無効化させたい場合はどうすればいいかについて考えてみます。
これは基本的にはさっきと同じでv-bindを使えば解決すると思います。
propsのdisabled変数をBooleanで定義して値が入ってくればButtonをdisabledにしています。
無効化したくない時はそもそも変数を指定しなくても問題なくしてあります。
Buttonのサイズを変更したい
最後はButtonのサイズについて考えてみたいと思います。
横幅と高さを変数で受け取ることでサイズを動的に変更出来るようにしました。
ですがこれもあまりいい方法だとは思えません。
Atomic Designの利点としてデザインの統一化を行えることが挙げられると思いますが、
上記の方法だと統一化は行えるでしょうか?
指定の自由度が高すぎてサイズがバラバラのButtonがたくさん出来てしまいかねません。
なので自分はButtonの色を変更する際と同じ方法を用いています。
Buttonのサイズについてもあらかじめデザインが定義されているものではないかと思うので。
large, medium, smallの3種類のサイズを定義してみました。
これでButtonのサイズも統一化出来ると思います。
設定されたButton一覧
作成したButtonコンポーネント、Iconコンポーネントで作成されたButtonの一覧はこんな感じになりました。
ButtonコンポーネントとIconコンポーネントだけで様々なButtonを作ることが出来ました。
最後に
思った以上に長くなってしまいましたが、
今回この記事に書いたことはあくまで自分なりの理解なので、
ここは間違ってるとかこうした方がいいとかもしくは自分はこんな整理でやってるとかコメントいただけると嬉しいです。
Button以外のコンポーネントについても考えがまとまったら記事を書いてみようと思います。
RailsのActive Adminで簡単な管理画面を作ってみた話
最近Railsを一度触ってみたかったので簡単なブログサービスを作ってました。
その中で管理画面の作成にActive Admin
を使ったので備忘録として残しておこうと思います。
環境
- Ruby 2.5.3
- Ruby on Rails 5.2.1
Active Adminとは
ブログとかの編集画面(記事の作成とか編集とか)みたいなものをほぼ自動で作成してくれるruby gemのことです。
すごく便利で導入もかなり簡単。
ブログとかなら記事を管理するModelを作成⇨Active Admin導入ってすると、
記事の新規作成、更新、削除の管理画面がたちまち完成するといった感じです。
今回の備忘録
導入の細かい話はググればいくらでも出てくるので特にしません。
今回ブログを作る上で、
このはてなブログやQiitaのようにMarkDownでの記事の記述と、
それを編集画面でリアルタイムプレビュー出来る機能を実装したかったのでデフォルトの管理画面のカスタマイズを行いました。
その際の備忘録です。
自分は記事を管理するarticle
というModelを作成してそれを管理画面で編集するようにしているのでその前提で話を進めます。
formをカスタマイズ
記事の新規作成画面と編集画面では共通のform
が使用されているのでそれをカスタマイズします。
まずはapp/admin
のarticle.rb
ファイルを修正します。
Active Admin
を導入した際に自動生成されているはず。
以下の一行を追加
form partial: "form"
"form"
と指定しているのがファイル名なので、
app/views/admin/article
配下に_form.erb
ファイルを作成します。
以下はformのテンプレートです。
<%= semantic_form_for [:admin, @article], builder: ActiveAdmin::FormBuilder do |f| %> <fieldset class="inputs"> <ol> <%= f.input :name %> </ol> </fieldset> <fieldset class="actions"> <ol> <li><%= f.submit "保存" %></li> <li class="cancel"><%= link_to("取り消す", admin_users_path)%></li> </ol> </fieldset> <% end %>
inputsクラス内に必要なinput項目を追加していくだけです。
<input>
タグで記述することも可能です。
一覧画面をカスタマイズする場合
一覧画面のカスタマイズ方法も記載しておきます。
先程と同様にapp/admin
のarticle.rb
ファイルを修正します。
formのカスタマイズと同じくerbファイルを作成して読み込ませることもできますが、
一覧画面に表示する項目の変更だけしたい場合などは以下の方法でも可能です。
index do column :id column :published column :created_at end
index do 〜 end
の間に表示したいカラムを指定して表示項目を変更できます。
最後に
Active Admin
初めて触りましたが導入までがすごく簡単で早いです。
Railsは優秀なgemがたくさんあって驚きました。
ただカスタマイズの仕方については少し苦戦したので書き残しました。。。
他にもactionなども自前のControllerでカスタマイズしたり出来るようです。
docker-composeを使ってみる
docker-composeを使っての環境構築を行なってみたのでその備忘録。
環境
構築する環境は以下。
- Python3.7.1
- Redis5.0.2
Dockerfileの作成
Python用のDockerfileを作成します。
FROM python:3.7 RUN pip install redis RUN pip install hiredis
installしてるのはredis
とhiredis
のみです。
必要なものがあれば適宜追記で。
docker-compose.ymlの作成
docker-compose.ymlを以下のように作成します。
version: '2' services: app: build: . ports: - "5000:5000" volumes: - .:/app links: - redis depends_on: - redis tty: true redis: restart: always image: redis:5.0.2 volumes: - ./data/redis:/data command: redis-server --appendonly yes
service
としてapp
とredis
を記載しています。
app
build:
上記で作成したDockerfile
のパスを指定します。ports:
右側にコンテナ内で使用するポート、左側にそれとマッピングさせるホストのポートを指定します。
ホストのポートを指定しない場合はランダムで割り振られるようです。volumes:
コンテナ内のディレクトリをマウントする際に指定します。
今回は/app
をマウントしています。
左側にはホストのディレクトリを指定。links:
PythonからRedisを使用出来るようにredis
を記載しています。depends_on:
service
の依存関係を指定します。
app
より先にredis
が立ち上がるようにしています。tty:
ポート待受とかしていないコンテナの場合、docker-compose upで起動してもすぐに終了してしまいます。
ttyをtrueに設定しておくことでコンテナが起動し続けます。
redis
restart:
OSの起動時にコンテナを自動起動させるためにalways
を指定しています。image:
Dockerイメージを記載。
redis:5.0.2
を指定しています。volumes:
/data
をマウントすることでデータを永続化させることが出来るようです。command:
command:
を指定することでデフォルトのコマンドの書き換えを行っています。
--appendonly yes
を指定しないとデータが作られないようです。
ビルド
作成したdocker-compose.ymlをビルド
$ docker-compose build
最後にSuccessfully
と表示されていればOK。
起動
$ docker-compose up
これでPython、Redisの各コンテナが立ち上がれば構築完了
動作確認
まずはRedisコンテナに入って確認します。
$ docker exec -it [コンテナ名、またはコンテナID] /bin/bash
コンテナに入れたらredis-cliを使用してみます。
$ redis-cli 127.0.0.1:6379> SET test test OK 127.0.0.1:6379> GET test "test" 127.0.0.1:6379>
次はPythonからRedisに接続してみます。 Redisコンテナで以下のコマンドを実行してIPを取得します。
$ hostname -i 172.20.0.2
以下のapp.py
ファイルを作成
import redis # Redisに接続 pool = redis.ConnectionPool(host='172.20.0.2', port=6379, db=0) r = redis.StrictRedis(connection_pool=pool) r.set('key', 'value') print(r.get('key'))
host
に上記で調べたIPを設定。
app.py
を実行。
$ python app.py
b'value'
PythonからもRedisが実行出来ることが確認できました。
これで完了です。
Pythonで登録したRSSの新着記事をSlackで受け取るツールを作成してみた
定期購読したいサイトのRSS登録を行い、
新着記事が存在した場合はそれをSlackへ通知するツールをPythonで作成してみました。
機能
ソースはgithubにも上げているので詳しくはそちらを参照下さい。 github.com
RSS登録機能
Slack上からスラッシュコマンドで新規RSSを登録する機能
RSS情報はRedisのハッシュを使って以下のように管理
('RSSから取得したブログタイトル', 'url', 'スラッシュコマンドから受け取ったRSSURL') ('RSSから取得したブログタイトル', 'previous_time', '最後に取得した最新記事の作成日時')
ここに登録したprevious_time
より作成日時が新しい記事のみ取得してSlackへ送信しています。
登録コマンドで送られてくるRSSURLのチェック
# URL形式チェック if re.match(r'^https?:\/\/', url): rss = feedparser.parse(url) … else: msg = 'URLは http://、https:// から記述してください。'
http://
、https://
で始まる文字列でない場合はエラーメッセージを返却
RSSを読み込んだ結果の判定も実施
rss = feedparser.parse(url) # パースが成功している場合のみ処理を行う if rss.bozo == 0: title = rss.feed.title self.redis.add(title, url, self.time_format(rss.entries[1].published)) msg = 'RSS登録完了: {}'.format(title) else: # パースに失敗した場合はエラーを出力して処理続行 print('url: {}, error: {}'.format(url, rss.bozo_exception)) msg = 'URLが存在しないか、またはRSSとして読み取り出来ませんでした。'
feedparser
から返却されるbozo
で判定
bozo
が0ならパース成功、
bozo
が1ならパース失敗
RSS一覧表示機能
Slack上からスラッシュコマンドで登録されているRSSの一覧表示を行う機能
# RSSの一覧を表示 msg = 'RSSList : \n - ' + '\n - '.join([key.decode() for key in self.redis.get_key_all()])
Redisからkeyの一覧を取得してmsgに詰めているだけです。
見た目が悪かったので1タイトルずつ改行して表示させています。
RSS削除機能
Slack上からスラッシュコマンドで登録されているRSSを削除する機能
一覧表示機能で登録されているRSSを確認して削除するイメージです。
# RSS削除を実施 title = params[b'text'][0].decode() self.redis.delete(title) msg = 'RSS削除完了: {}'.format(title)
スラッシュコマンドで受け取ったタイトルをkeyにしてRedisから削除しているだけです。
新着記事通知機能
登録されているRSSを順番に読み込んでいき、
新着記事が存在する場合のみSlackへ通知を行っています。
登録されているkey分繰り返し処理
# 登録されたRSS数分処理を行う for key in self.redis.get_key_all(): # RSSのURL、最後に取得した記事の作成日時を取得 rss_config_dict = self.redis.get(key) url = rss_config_dict[b'url'].decode() # RSS読み取り rss = feedparser.parse(url) …
Redisから取り出したkeyに紐付くRSSURLを読み込んでパースしています。
Slackへ送信するattachments
を作成
def create_attachments(self, rss, last_items, key): """取得した記事からattachmentsを生成""" attachments = [] for i in range(last_items, -1, -1): # 取得した記事の作成日時を保存 published = rss.entries[i].published self.redis.update_previous_time(key, self.time_format(published)) summary = rss.entries[i].summary # 投稿した記事をattachmentに追加 attachments.append( { 'title' : '<{}|{}>'.format(rss.entries[i].link, rss.entries[i].title), # タイトルと記事のリンク 'text' : re.compile(r'<[^>]*?>').sub('', summary) if summary is not None else '', # 記事のサマリー 'footer' : self.time_format(published) # 記事の投稿時間 } ) return attachments
last_items
には取得する新着記事が1件の場合は0、2件の場合は1が入ってくるイメージです。
Redisのprevious_time
を記事の作成日時で更新します。
これで次回実行時に同じ記事を取得しないようにしています。
Slackへ送信しているのはtitle
、link
、summary
、published
のみです。
それをスレッド化して送信すると以下のようになります。
スレッドを開くと以下のようになっています。
こんな感じでSlackからブログの購読が可能になりました。
自分はこれをcronで毎朝7時に実行されるようにして通知を受け取っています。
Pythonで作成したWebAPIをherokuにデプロイ
Pythonで作成したWebAPIをherokuにデプロイしてみたのでその備忘録
準備
まずはherokuにデプロイするために必要なファイルの準備を行います。
$ pip install gunicorn $ pip freeze > requirements.txt $ echo python-3.7.1 > runtime.txt $ echo web: gunicorn [実行ファイル]:app --log-file=- > Procfile
・アプリケーションサーバとしてgunicorn
をインストール
・freeze
を使用してインストール済みパッケージをrequirements.txt
に記載
・runtime.txt
に使用しているPythonのバージョンを記載
・Procfile
にはどのプロセスを使用するかを定義、[実行ファイル]には実行する.pyファイルを記載
git管理
git⇨herokuへデプロイするのでgitで管理できるようにします。
$ git init $ git add . $ git commit -m "initial commit"
herokuへデプロイ
git⇨herokuへデプロイを行います。
$ heroku login $ heroku apps:create $ git push heroku master
herokuアカウントを作成していない場合は作成。
$ heroku apps:create
で作成した場合はランダムでアプリケーション名が設定されます。
必要な場合はアプリケーション名を指定する。
エラーなくpush
できれば完了です。
PythonでQiitaの新着記事を取得してSlackへ連携してみる
QiitaAPIで自分がフォローしているタグが付いた記事を最新10件取得して、
それをさらにSlackへ連携するツールを作成してみました。
前提
・Pythonは3.7.1を使用しています。
・Python環境構築については特に触れません。
・Slack APIの作成部分についても触れません。
・ソースはgithubにも上がっています。
github.com
フォローしているタグ一覧を取得
まずはQiitaAPIから自身がフォローしているタグの一覧を取得します。
以下のURLで取得出来ます。
http://qiita.com/api/v2/users/[Qiitaアカウント]/following_tags?page=1
コードは以下です。
import requests # Qiitaアカウント qiita_user = '' # 自分がfollowしているタグの一覧を取得 url = 'http://qiita.com/api/v2/users/' + qiita_user + '/following_tags' params = {'page': '1'} following_tags = requests.get(url, params=params).json()
following_tagsに取得結果が入っています。
中身はこんな感じです。
[ { 'followers_count': 2106, 'icon_url': , 'id': 'React', 'items_count': 1999 } ]
4つフィールドが取得出来ましたが、今回使用するのは「id」のみです。
これがタグ名になっています。
Qiita新着記事を取得
次はQiitaAPIを使用して新着記事を取得する部分です。
以下のURLで取得出来ます。
http://qiita.com/api/v2/items?page=1&per_page=10
これで新着記事が10件取得出来ます。
「per_page」の件数を変更すると取得件数が変わります。
import requests # QiitaAPIを使用して新着記事を10件取得 url = 'http://qiita.com/api/v2/items' params = {'page': '1', 'per_page': '10'} new_articles = requests.get(url, params=params).json()
new_articlesに取得結果が入っています。
中身はこんな感じです。
[ { 'rendered_body': , 'coediting': , 'comments_count': , 'created_at': , 'group': , 'id': , 'likes_count': , 'private': , 'reactions_count': , 'tags': [ { 'name': 'Python', 'versions': [] } ], 'title': , 'updated_at' : , 'url': , 'user': { 'description': , 'facebook_id': , 'followees_count': , 'followers_count': , 'github_login_name': , 'id': , 'items_count': , 'linkedin_id': , 'location': , 'name': , 'organization': , 'permanent_id': , 'profile_image_url': , 'twitter_screen_name': , 'website_url': }, 'page_views_count': } ]
例によってほとんどの項目は使用しません。。。
ひとまずこれでフォローしているタグの一覧と新着記事の取得が出来ました。
記事の絞り込み
では次にフォローしているタグが含まれる記事のみに絞り込みます。
絞り込みは記事の取得時のURLでクエリパラメーターを追加してあげることで可能です。
http://qiita.com/api/v2/items?page=1&per_page=10&query=[ここに条件を指定]
先程取得してきたタグの一覧を条件に含める部分のソースは以下になります。
# 自分がfollowしているタグの一覧を取得 url = 'http://qiita.com/api/v2/users/' + qiita_user + '/following_tags' params = {'page': '1'} following_tags = requests.get(url, params=params).json() # フォローしているタグを記事取得時の検索で使用出来るようにする # 記事取得時のqueryにtag:[タグ名]でそのタグが含まれる記事を取得出来るので、 # フォローしているタグのいずれかを含むものを取得出来るようにORでつなげていく query = '' for i in range(len(following_tags)): query = query + 'tag:' + following_tags[i]['id'] + " OR " # QiitaAPIを使用してフォローしているタグを含む新着記事を10件取得 url = 'http://qiita.com/api/v2/items' params = {'page': '1', 'per_page': '10', 'query': query.rstrip(' OR ')} new_articles = requests.get(url, params=params).json()
タグが含まれているかの条件はtag:[タグ名]で可能です。
検索時の条件をORで繋げて指定することが出来ます。
ここまでで自分がフォローしているタグを含む新着記事10件を取得することが可能になりました。
Slackへ送信
次はSlackへ取得した記事を送信する部分です。
10件分の記事を送信するので、
1回分の送信をスレッド化してメッセージが長くならないようにしてみます。
まずは最初にスレッドタイトルの送信部分です。
# slack送信用のHeader headers = { 'Content-Type' : 'application/json; charset=utf-8', 'Authorization' : 'Bearer ' + slack_token } # slackへ送信(スレッドタイトル) web_hook_url = "https://slack.com/api/chat.postMessage" res = requests.post(web_hook_url, data = json.dumps({ 'channel': slack_channel, # チャンネル 'attachments': [{ 'title' : 'Qiita新着投稿' }], # 通知内容 }), headers = headers)
「Qiita新着投稿」というattachmentsだけを送信するシンプルなものになっています。
headerに「Content-Type」と「Authorization」を指定しています。
「slack_token」にはSlackAPIのトークンを発行したものを指定しています。
「slack_channel」には投稿するチャンネルを指定しています。
次にスレッド化して記事を送信します。
先程送信したスレッドタイトルのレスポンスから「ts」を取得します。
それを記事送信時に「thread_ts」にセットすることで紐付けることが可能。
コードは以下です。
# slackへ送信(スレッドタイトルに紐付けて記事をスレッド化) # スレッドタイトルを送信した際のレスポンスの'ts'を'thread_ts'にセットすることで紐付けしている requests.post(web_hook_url, data = json.dumps({ 'channel': slack_channel, # チャンネル 'attachments': attachments, # 通知内容 'thread_ts' : res.json()['ts'], # スレッドタイトルの'ts' }), headers = headers)
「attachements」には取得した記事を入れています。
生成のロジックは以下です。
# 取得した記事を1つずつattachmentに詰めていく attachments = [] for i in range(len(new_articles)): # 記事のタグを取得 tags = 'タグ:' for tag in new_articles[i]['tags']: tags = tags + '[' + tag['name'] + ']' # 投稿した記事をattachmentに追加 attachments.append( { 'title' : '<' + new_articles[i]['url'] + '|' + new_articles[i]['title'] + '>', # タイトルと記事のリンク 'text' : tags, # 記事のタグ 'author_name' : new_articles[i]['user']['id'], # 投稿者名 'author_link' : 'https://qiita.com/' + new_articles[i]['user']['id'], # 投稿者のQiitaページへのリンク 'author_icon' : new_articles[i]['user']['profile_image_url'], # 投稿者のQiitaアイコン 'footer' : new_articles[i]['updated_at'] # 記事の投稿時間 } )
記事自体はセットせず、記事タイトルとリンクなどを送信しています。
これで全て完成です。
実際にslackへ送信すると以下のようになります。
ちゃんとスレッド化されています。
スレッドを開くと以下のような感じです。
見やすく表示させることが出来ました。
RustでSlackにカスタム絵文字を登録するツールを作成してみた
Slackのカスタム絵文字
以下みたいなアクションで使う絵文字
このカスタム絵文字の登録をSlackから実施出来るようにRustでAPIを実装してみました。
このソースはgithubにもあげています。 github.com
概要
以下のようなAPIになっています。
・スラッシュコマンドで画像のURLと登録絵文字名をリクエストとして受け取る
・画像のURLからアップロードする画像を取得する
・Slackの絵文字アップロード画面をPOSTで叩く
Slackではカスタム絵文字登録を行うAPIは公開されていませんでした。
なので、Slackの画像アップロード画面を直接APIから叩くような仕様になっています。
api_tokenの取得方法について
Slackの画像アップロード画面を叩く際にapi_tokenが認証に必要になります。
api_tokenは以下から取得できます。
https://[自身のワークスペース].slack.com/customize/emoji
ページを開いたらF12を押してデベロッパーツールを開きます。
その状態でページを再読み込みし、上部のSourcesを選択します。
そこで表示されるHTML内のapi_token
を使用しています。
実装
リクエストの分解
// 空白で文字列を分割する 絵文字画像URL 登録絵文字名 let v: Vec<&str> = params.get("text").unwrap().split(' ').collect(); let emoji_url: &str = v.get(0).unwrap(); let emoji_name: &str = v.get(1).unwrap();
スラッシュコマンドで送られてきたリクエストからparamsを取得、
そのparamsを空白で分割し、1つ目を画像URL、2つ目を絵文字登録名として取り出します。
画像の取得
// URLから画像を取得してローカルに保存する fn download_image(url: &str) { // 画像データの取得 let image = reqwest::get(url).unwrap(); let file = File::create(FILE_NAME).unwrap(); let mut br = BufWriter::new(&file); for byte in image.bytes() { br.write(&[byte.unwrap()]).unwrap(); } br.flush().unwrap(); // 保存した画像に読み取り権限を付与 let mut perms = file.metadata() .expect("メタデータの取得に失敗しました。") .permissions(); perms.set_readonly(true); file.set_permissions(perms) .expect("権限付与に失敗しました。"); }
リクエストから取得した画像URLで画像を取得し、
それを一時的にローカルに保存しています。
後でSlackにアップロードするために読み取り権限を付与しています。
絵文字アップロード
// 絵文字のアップロードを行う fn upload_emoji(url: &str, form: Form, emoji_name: &str, api_token: &str) -> impl Future<Item = String, Error = Error> { // カスタム絵文字アップロードリクエスト let mut res = Client::new() .post(url) .query(&[ ("mode", "data"), ("name", emoji_name), ("token", api_token)]) .multipart(form) .send() .unwrap(); match res.text() { Err(e) => fut_ok(String::from(format!("アップロード失敗:{:?}", e))), Ok(result) => { let v: Value = serde_json::from_str(result.as_str()).unwrap(); // エラー判定 match v["ok"].as_bool() { Some(true) => fut_ok(String::from(format!("アップロード完了::{}:", emoji_name))), Some(false) => fut_ok(String::from(format!("アップロード失敗:{}", v["error"]))), None => fut_ok(String::from(format!("アップロード失敗:{}", v["error"]))), } } } }
Slackへの絵文字のアップロードを行なっています。
crateはrequwest
を使用しています。
ここで設定しているemoji_name
はスラッシュコマンドから、
api_token
は上で記載した取得方法で取得したものを使用しています。
Slackからのレスポンスも判定しており、
正常に登録できた場合は応答として「アップロード完了:[登録した絵文字]」が、
登録に失敗した場合は「アップロード失敗:[エラーメッセージ]」が返却されるようになっています。
使用イメージ
addemoji
というスラッシュコマンドを使用しています。
cat_icon
という名前で登録してみます。
登録が完了すると以下のように表示されます。
これでスマホのSlackアプリからも登録出来るようになりました。