laravelのfactoryでコメントとコメントの子的なのを作成する
どんなの作るか
ブログのコメントとかでよくある 記事のコメント コメントのコメント とかできるやつ
コード
Factory
<?php namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; use App\Models\Comment; /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Comment> */ class CommentFactory extends Factory { private static int $sequence = 1; private static int $user_id = 1; /** * Define the model's default state. * * @return array<string, mixed> */ public function definition(): array { return [ 'user_id' => fn () => self::$sequence++, 'question_id' => fake()->numberBetween(1,100), 'parent_id' => NULL, 'body' => fake()->realText($maxNbChars =100), ]; } public function child(): static { return $this->state(fn (array $attributes) => [ 'parent_id' => Comment::all()->random(), ]); } public function configure() { return $this->afterMaking(function (Comment $comment) { // })->afterCreating(function (Comment $comment) { if (self::$user_id !== $comment->user_id) { self::$user_id = $comment->user_id; self::$sequence = 1; } }); } }
Seeder
<?php namespace Database\Seeders; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; use App\Models\Comment; class CommentSeeder extends Seeder { /** * Run the database seeds. */ public function run(): void { Comment::factory() ->count(1000) ->create(); for ($i = 0; $i < 3; $i++) { Comment::factory() ->count(1000) ->child() ->create(); } } }
こうすれば
1000個のユーザーで
1000件の記事へのコメント
1000件*3のコメントへのコメントが作成できる
各ユーザーは4回書き込んだことになる
参考
google workspaceでドメインの所有権の証明ができない
症状
Gmailを有効にするところからやるといつまで立ってもできなかった
以下のようなエラーが出る
Gmail を有効にできませんでした。以下の点にご対応のうえ、もう一度お試しください。
デバイスがありません
よくわからんがこれでメールの送受信はできる
https://admin.google.com/u/0/ac/signup/setup/v2/verify/txt
ここからTXTレコードで証明する方法をやる
これは一瞬で終わった
この後にgmailを有効にするページから
https://admin.google.com/u/5/ac/signup/setup/v2/gettingstarted
同じようにMXレコード登録する
エラーが出るけど、この状態でメールの送受信はできる
謎
そして半日が過ぎたら勝手に証明終わってた
https://toolbox.googleapps.com/apps/dig/
googleのdig使って反映されているのを確認してたけど、これに反映されているからといってすぐできるものでもなかったらしい
vscodeのvimで、gg=Gでインデントできない
環境
formatterにLaravel Blade formatter
を使用してblade.php
をインデントしようとしている
事象
gg=G
を入力したら
Configure Default Formatter Extension 'Laravel Blade Formatter' is configured as formatter but it cannot format 'Blade'-files
と出て、インデントできない
わかったこと
Ctrl+Shift+p
で出てくるところに以下の文を打ち込むとできる場合とできない場合がある
インデント可能 editor.action.formatDocument インデント不可 editor.action.formatSelection
今後
とりあえずインデント可能なやつのショートカットキーは
Alt+Shift+f
なのでしばらくこれで過ごす
選択したものをインデントするときの処理でなんかだめっぽい Laravel Blade formatter側がだめな気がする
ちなみに
extensionのvimで呼び出している箇所は
https://github.com/VSCodeVim/Vim/blob/master/src/actions/operator.ts
だと思う
@RegisterAction class FormatOperator extends BaseOperator { public keys = ['=']; public modes = [Mode.Normal, Mode.Visual, Mode.VisualLine, Mode.VisualBlock]; public async run(vimState: VimState, start: Position, end: Position): Promise<void> { // = operates on complete lines vimState.editor.selection = new vscode.Selection(start.getLineBegin(), end.getLineEnd()); await vscode.commands.executeCommand('editor.action.formatSelection'); let line = vimState.cursorStartPosition.line; if (vimState.cursorStartPosition.isAfter(vimState.cursorStopPosition)) { line = vimState.cursorStopPosition.line; } const newCursorPosition = TextEditor.getFirstNonWhitespaceCharOnLine(vimState.document, line); vimState.cursorStopPosition = newCursorPosition; vimState.cursorStartPosition = newCursorPosition; await vimState.setCurrentMode(Mode.Normal); } }
画像の2点をクリックしてcropした画像と座標を手に入れるpython
前書き
画像の切り抜きとそれの座標がほしかった
実際に書いたもの
from PIL import Image, ImageTk, ImageGrab import numpy as np import cv2 import os import sys class img2crop(): def __init__(self): os.chdir(os.path.dirname(os.path.abspath(__file__))) self.x1=self.y1=self.x2=self.y2=self.f=0 if len(sys.argv) <= 1: self.paste_clipboard() else: self.open_img() def pil2cv(self,image): ''' PIL型 -> OpenCV型 ''' new_image = np.array(image, dtype=np.uint8) if new_image.ndim == 2: # モノクロ pass elif new_image.shape[2] == 3: # カラー new_image = new_image[:, :, ::-1] elif new_image.shape[2] == 4: # 透過 new_image = new_image[:, :, [2, 1, 0, 3]] return new_image def onMouse(self,event, x, y, flags, params): if event == cv2.EVENT_LBUTTONDOWN: if not self.f: self.x1=x self.y1=y self.f=True else: self.x2=x self.y2=y if (self.x1 >= self.x2 and self.y1 >= self.y2): t = self.x1 self.x1 = self.x2 self.x2 = t t = self.y1 self.y1 = self.y2 self.y2 = t # 出力文字列 print(f'{self.x1},{self.y1},{self.x2},{self.y2}') self.f=False cv2.imshow(f'img2crop - {self.x1},{self.y1},{self.x2},{self.y2}', self.cv_im[self.y1:self.y2,self.x1:self.x2]) cv2.imwrite(f'{self.x1},{self.y1},{self.x2},{self.y2}.png', self.cv_im[self.y1:self.y2,self.x1:self.x2]) def paste_clipboard(self): self.im = ImageGrab.grabclipboard() if isinstance(self.im, Image.Image): self.view_im() else: print(u"クリップボードは画像ではありません") input() def open_img(self): try: self.im = Image.open(sys.argv[1]) self.view_im() except: print(u"ファイルは画像ではありません") input() def view_im(self): self.cv_im = self.pil2cv(self.im) cv2.imshow('img2crop', self.cv_im) cv2.setMouseCallback('img2crop', self.onMouse) cv2.waitKey(0) if __name__ == '__main__': img2crop()
nicehashの出金手数料を通知してくれるdiscord botをgasで書いた
前書き
出金手数料通知してくれるdiscordサーバがあったので入っていたのですが、通知してくれなくなったので自分で書いた
サーバは公開したくないのでソースコードだけ貼っときます
ソースコード
// ここの箇所は各自変更する必要あり const discordWebHookURL = "ディスコードのウェブフックURLをコピペするところ";
// nicehashの手数料を取得 function get_nicehash_fee(coin='BTC'){ var response = UrlFetchApp.fetch('https://api2.nicehash.com/main/api/v2/public/service/fee/info') var fee_info = JSON.parse(response.getContentText()); return(fee_info['withdrawal']['BITGO']['rules'][coin]['intervals'][0]['element']['sndValue']) } // discordにウェブフックを使ってbotメッセージを送信 function send_discord(msg='', everyone = false){ const discordWebHookURL = "ディスコードのウェブフックURLをコピペするところ"; if(everyone){ msg = '@everyone' + msg } const message = { "content": msg, "tts": false } const param = { "method": "POST", "headers": { 'Content-type': "application/json" }, "payload": JSON.stringify(message) } UrlFetchApp.fetch(discordWebHookURL, param); } // 現在の時刻の文字列を返す function get_now(){ var date = new Date(); return Utilities.formatDate( date, 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss'); } // main function myFunction() { // 取得するコインの種類 var coin = "BTC" // 前回の手数料 var properties = PropertiesService.getScriptProperties(); var fee_before = properties.getProperty("fee_before"); var res_fee = get_nicehash_fee(coin); // 表示するメッセージの生成 msg = '['+get_now()+'] '+coin+': '+res_fee; Logger.log(msg + ', before:' + fee_before); // 前回と違えば表示する if(fee_before != res_fee){ send_discord(msg, everyone = res_fee <= 0.000001); } // 前回の手数料を保存 properties.setProperty("fee_before", res_fee); }
使い方
1.gasを作成
googleドライブを開いて、新規 -> その他 -> Google Apps Script
ここに先程のソースコード貼り付ける
2.ウェブフックURLの取得と書き換え
導入したいdiscordサーバの設定 -> 連携サービス -> ウェブフック -> 新しいウェブフック -> ウェブフックURLをコピー
これで得たURLを先程の貼り付けたソースコードの変更する必要のある場所を書き換える
3.トリガーの設定をして実行
gasでトリガー -> トリガーを追加 を選んで、実行する関数は「myFunction」
分ベースのタイマー、1分おきとかその辺は好みで設定して完了
動作について
前回と違う手数料に変わった時だけdiscordに通知されます
手数料が0.000001BTC以下だと@everyoneをつけて通知します
discordなんて使いたくねーよ
なんて人がいたらpythonで1分ごとに取得するものをgasで書く前に試しに書いたのでどうぞ
0.000001BTC以下だったらビープ音鳴らすなりしたらいいと思う(そこまで書いてません)
import json import urllib.request import datetime import time while True: url = 'https://api2.nicehash.com/main/api/v2/public/service/fee/info' req = urllib.request.Request(url) with urllib.request.urlopen(req) as res: dt_now = datetime.datetime.now() fee_info = json.load(res) coin = 'BTC' print("[{}] {}: {:.8f}".format(dt_now.strftime('%Y-%m-%d %H:%M:%S'), coin, fee_info['withdrawal']['BITGO']['rules'][coin]['intervals'][0]['element']['sndValue'])) time.sleep(60)
discordのアクティビティステータス認証済みを削除したり名前を変える
追記
デベロッパーツールは
%appdata%/discord/settings.json
に以下の文を書き足さないと開けなくなりました
"DANGEROUS_ENABLE_DEVTOOLS_ONLY_ENABLE_IF_YOU_KNOW_WHAT_YOURE_DOING": true
やり方
discordを開いた状態でCtrl+Shift+Iを押してデベロッパーツールを起動する
F1キーを押して設定を開き、Preferencesの右下の方にあるEnable Local Overridesを有効にする
Networkタブを開きCtrl+Rでリロードする
その中からdetectableを探し出し保存し、Request URLもメモする
自分の場合は
https://discordapp.com/api/v9/applications/detectable
Local Overridesに使うフォルダを作成し、先程のRequest URLのドメインからのPATHと同じようなフォルダ階層を作成する
D:\discord_local_overrides\discordapp.com\api\v9\applications
このapplicationsの中に先ほどダウンロードしたdetectableを保存する
拡張子がついている場合は削除してファイル名を「detectable」だけにする
discordの画面に戻りSourcesタブ>Overridesタブ>+Select folder for overridesからLocal Overridesに使うフォルダを指定する
先程の例でいうと
D:\discord_local_overrides
Overridesタブが見当たらない場合は「>>」ボタンを押すと出てくる
追加したのに変化が見られない場合はデベロッパーツールを閉じて開いてやると出てくる(Ctrl+Shift+Iを2回)
detectableを編集
全ての認証済みをなくす
[]
だけにする
するとdiscord内で名前変更可能になるが、アイコンは表示されなくなる
名前を変更する
表示される名前で検索してヒットしたところを変更する
具体的な場所については下に記載
detectableの例
{ "description": "A popular mobile collectible card game now also on PC.", "developers": [ { "id": "521816528895737856", "name": "Cygames, Inc." } ], "executables": [ { "is_launcher": false, "name": "shadowverse.exe", "os": "win32" } ], "hook": true, "icon": "748e96fabbfc42013d1806fe220f891d", "id": "363409615620407316", "name": "名前を変更する場所", "publishers": [ { "id": "521816528895737856", "name": "Cygames, Inc." } ], "splash": "d72314c7581c59293066459ed7baf8cf", "summary": "A popular mobile collectible card game now also on PC.", "third_party_skus": [ { "distributor": "steam", "id": "453480", "sku": "453480" } ], "verify_key": "6ee784f97ba25886290820169f26c9432bbde09a226b559c6749fca4684db476" },
1部のゲームだけ消したい場合はこれごと削除
後書き
取得urlが変わることがあるので注意
その場合はフォルダ名の変更の必要あり
アイコンの変更もできたが再現性がなかったためよくわからなかった
認証済みゲームなのかの認識は実行ファイル名でしているので、全く関係ないゲームを、例えば「shadowverse.exe」にして実行するとshadowverseとして認識できる
javascriptの無名関数で定義されちゃったEventListenerを消す
前書き
あるサイトで右クリックを使いたかったけど右クリックが前ページに戻るって動作になっててコンテキストメニューが表示されなかった
調べると、無名関数で登録されてるせいで
element.removeEventListener("contextmenu", ここ);
ここのところが指定できなくて困ったのでなんとかする
たぶん、chromeでしか使えない
サンプルコード
var x = document.getElementsByClassName("reader")[0]; x.removeEventListener("contextmenu", getEventListeners(x).contextmenu[0].listener)
説明
1行目は、右クリックしたい要素を変数にいれる
2行目は、さっき「ここ」って書いた場所に書いてる奴
getEventListeners(x).contextmenu[0].listener
が無名関数を特定する処理