331 Commits
Author SHA1 Message Date
kibi 00db7c6113 path!
fetch npm modules / f0ck the f0cker (push) Successful in 17s
2024-02-20 01:26:59 +01:00
kibi 050c9e66c8 path?
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2024-02-20 01:16:40 +01:00
kibi d134e4d6be fixing imgur
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2024-02-20 01:13:41 +01:00
kibi d4ac155c68 making me latest change return fuck instagram
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2024-02-19 21:52:20 +01:00
kibi afb38f9af5 removing insta link message
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2024-02-19 21:02:44 +01:00
kibi 604e54f238 adding instagram video support
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2024-02-19 20:56:39 +01:00
kibi 84c58479eb pure verzweiflung mit js
fetch npm modules / f0ck the f0cker (push) Successful in 20s
2024-02-18 21:33:53 +01:00
Flummi d69f9b8427 top10
fetch npm modules / f0ck the f0cker (push) Successful in 26s
2024-02-18 02:18:43 +01:00
Flummi 6c6f739110 merged :>
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2024-01-03 14:50:06 +01:00
Flummi f674fe2a40 revert
fetch npm modules / f0ck the f0cker (push) Successful in 17s
2024-01-03 14:47:55 +01:00
Flummi f32d712dff fu
fetch npm modules / f0ck the f0cker (push) Successful in 21s
2024-01-03 14:47:22 +01:00
kibi 64943719da fixing paper theme login page
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-12-04 09:38:50 +01:00
kibi 4b78b70ede 1080p because I am sick of counting pixels
fetch npm modules / f0ck the f0cker (push) Successful in 25s
2023-12-02 22:55:13 +01:00
kibi 177e184621 css fixes
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2023-11-29 11:31:31 +01:00
kibi 1f42acb726 🦅🦅🦅
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2023-11-29 11:19:01 +01:00
kibi 6e561071ee jg
fetch npm modules / f0ck the f0cker (push) Successful in 17s
2023-11-29 06:15:14 +01:00
kibi 564f1bf530 steps lowered
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2023-11-29 06:12:43 +01:00
kibi d9e8b63004 .+dx
fetch npm modules / f0ck the f0cker (push) Successful in 20s
2023-11-29 06:00:39 +01:00
kibi c7329292ca opacity
fetch npm modules / f0ck the f0cker (push) Successful in 17s
2023-11-29 05:57:37 +01:00
schrumpel 8e9de0252e example config
fetch npm modules / f0ck the f0cker (push) Successful in 17s
2023-11-29 05:48:50 +01:00
schrumpel 1a4afd22ab integ
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-11-29 05:23:34 +01:00
schrumpel c13ca165c0 toggle
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2023-11-29 04:54:01 +01:00
schrumpel cba5fc0410 fader
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2023-11-29 04:47:41 +01:00
schrumpel 7a42e6ffce QoL
fetch npm modules / f0ck the f0cker (push) Successful in 17s
2023-11-29 04:46:21 +01:00
schrumpel 5e39c67147 fixes fixex fixes
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2023-11-29 03:18:27 +01:00
schrumpel 226c368945 Merge remote-tracking branch 'origin/dev'
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2023-11-29 01:31:22 +01:00
schrumpel 8dc448fe13 profile
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-11-29 01:27:51 +01:00
schrumpel b1c9b7de72 Merge remote-tracking branch 'origin/dev'
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-11-29 00:56:33 +01:00
schrumpel 89a93cc50c user profile
fetch npm modules / f0ck the f0cker (push) Successful in 20s
2023-11-29 00:56:21 +01:00
schrumpel a0c5f94921 Merge remote-tracking branch 'origin/dev'
fetch npm modules / f0ck the f0cker (push) Successful in 17s
2023-11-28 22:31:15 +01:00
schrumpel 95a40bd1c2 fixing f0ckgle
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-11-28 22:31:05 +01:00
schrumpel 56dcabfa94 f0ck profiles
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-11-28 22:29:18 +01:00
schrumpel 5d79243fe1 f0ck conflicts...
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-11-28 22:27:44 +01:00
schrumpel 267a1427c3 profile
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-11-28 22:24:40 +01:00
schrumpel 4ec8edfb37 Merge remote-tracking branch 'origin/dev'
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2023-11-28 20:25:19 +01:00
schrumpel 8b9677bb76 trying different directions
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-11-28 20:25:01 +01:00
schrumpel 68724890fd Merge remote-tracking branch 'origin/dev'
fetch npm modules / f0ck the f0cker (push) Successful in 19s
merging dev to master, wheelr
2023-11-28 20:17:58 +01:00
kibi 0dff028982 revert 0e793ecde5
fetch npm modules / f0ck the f0cker (push) Successful in 20s
revert gives me three thumbnails on my phone, should be good
2023-11-28 19:16:01 +00:00
schrumpel d9ced4a896 wheeler fix
fetch npm modules / f0ck the f0cker (push) Successful in 20s
2023-11-28 20:08:32 +01:00
schrumpel 0e793ecde5 gives me three thumbnails on my phone, should be good 2023-11-28 19:05:49 +01:00
kibi 1b350781f2 Merge remote-tracking branch 'origin/dev'
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-11-28 18:22:52 +01:00
kibi 84937c88a1 scroller
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-11-28 18:22:10 +01:00
schrumpel 1d84180603 -.-
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2023-09-02 02:29:46 +02:00
schrumpel a942fc0b94 f0ckgle ß49tjk
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2023-09-02 02:27:33 +02:00
schrumpel abf4ca92b1 f0ckgle easter egg
fetch npm modules / f0ck the f0cker (push) Successful in 17s
2023-09-02 02:17:12 +02:00
schrumpel cc73ee2162 vllt jetzt?
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-09-02 02:04:16 +02:00
schrumpel 6590f72b88 public/s/css/f0ck.css
fetch npm modules / f0ck the f0cker (push) Successful in 22s
2023-09-01 23:12:31 +02:00
Flummi 0b2f174ee8 readme: remove nsfw-detector
fetch npm modules / f0ck the f0cker (push) Successful in 14s
2023-08-01 17:31:22 +00:00
Flummi f9e10abb51 fix
fetch npm modules / f0ck the f0cker (push) Failing after 20s
2023-07-29 13:03:50 +02:00
Flummi 78a2524ce9 remove !ignore and add !f
fetch npm modules / f0ck the f0cker (push) Successful in 18s
2023-07-28 11:53:13 +02:00
Flummi 81e7d50a6b fix recovery order 2023-07-28 11:52:58 +02:00
Flummi 1380e45794 fix count
fetch npm modules / f0ck the f0cker (push) Successful in 19s
2023-07-02 13:22:19 +02:00
Flummi 9ca17e6fd4 Merge remote-tracking branch 'origin/master' into dev
fetch npm modules / f0ck the f0cker (push) Successful in 20s
2023-07-02 13:12:19 +02:00
Flummi 48596ad17a user profile lel
fetch npm modules / f0ck the f0cker (push) Successful in 16s
2023-07-02 13:11:31 +02:00
Flummi 85212051d0 small bugfix 2023-07-02 13:11:11 +02:00
Flummi 046a7460ca using the normalized tags
fetch npm modules / f0ck the f0cker (push) Successful in 21s
2023-06-09 15:31:59 +02:00
Flummi f2757f08af Merge branch 'dev' of git.lat:f0ck/f0ckv2 into dev
fetch npm modules / f0ck the f0cker (push) Successful in 21s
2023-05-31 01:08:40 +02:00
Flummi e2f15ce862 ranking schmanking 2023-05-31 01:08:23 +02:00
kibi aa3a4d0217 small change for p1nk css
fetch npm modules / f0ck the f0cker (push) Successful in 26s
2023-05-20 16:59:02 +02:00
Flummi 3074b0294a f0ck.sql user_options -> fullscreen
fetch npm modules / f0ck the f0cker (push) Successful in 21s
2023-05-10 01:32:07 +02:00
kibi fb48cccdbb ... xd
fetch npm modules / f0ck the f0cker (push) Successful in 30s
2023-05-07 18:41:12 +02:00
kibi 8dd54811ee fixing fixing fullscreen button color
fetch npm modules / f0ck the f0cker (push) Successful in 29s
2023-05-07 18:38:29 +02:00
kibi 460d4a84aa fixing fullscreen button color
fetch npm modules / f0ck the f0cker (push) Successful in 30s
2023-05-07 18:31:41 +02:00
kibi a9a5be9fb4 fadeIn effect
fetch npm modules / f0ck the f0cker (push) Successful in 30s
2023-05-06 13:50:41 +02:00
kibi b2c43e18bb static height for thumbnail in window title bar
fetch npm modules / f0ck the f0cker (push) Successful in 30s
2023-05-06 13:27:43 +02:00
Flummi 84104b58bc toggle fullscreen
fetch npm modules / f0ck the f0cker (push) Successful in 32s
2023-05-06 06:54:09 +02:00
Flummi a753a3f27d playercontrols svg 2023-05-06 06:53:32 +02:00
kibi 05c59717fd Merge remote-tracking branch 'origin/dev'
fetch npm modules / f0ck the f0cker (push) Successful in 16s
2023-05-03 22:42:04 +02:00
kibi 51cfc34f75 fixing weird font-rendering on windows
fetch npm modules / f0ck the f0cker (push) Successful in 15s
2023-05-03 22:41:41 +02:00
kibi a49ea84ac3 Merge remote-tracking branch 'origin/dev'
fetch npm modules / f0ck the f0cker (push) Successful in 14s
2023-05-03 22:08:46 +02:00
kibi 5fcad639dc amoled theme - genugtuung für die augen
fetch npm modules / f0ck the f0cker (push) Successful in 15s
2023-05-03 22:08:26 +02:00
kibi b12170382a Merge remote-tracking branch 'origin/dev'
fetch npm modules / f0ck the f0cker (push) Successful in 15s
2023-05-03 22:03:41 +02:00
kibi afba818021 small ui change
fetch npm modules / f0ck the f0cker (push) Successful in 16s
2023-05-03 22:02:37 +02:00
Flummi 5eeaa28612 issue #52
fetch npm modules / f0ck the f0cker (push) Successful in 15s
2023-05-03 05:29:32 +02:00
Flummi 2301d15abd cleaned up
fetch npm modules / f0ck the f0cker (push) Successful in 37s
2023-05-03 05:13:48 +02:00
Flummi fbf03bfbb4 wtf frontend
fetch npm modules / f0ck the f0cker (push) Successful in 15s
2023-05-03 04:53:22 +02:00
Flummi 63bd1104b7 missing ids
fetch npm modules / f0ck the f0cker (push) Successful in 37s
2023-05-03 04:25:18 +02:00
Flummi e47dfb38fb deleted f0cks in ranking
fetch npm modules / f0ck the f0cker (push) Successful in 36s
2023-05-03 04:08:59 +02:00
Flummi cea4a13b4c thumbnailer trigger !thumb
fetch npm modules / f0ck the f0cker (push) Successful in 36s
2023-05-03 04:01:55 +02:00
Flummi 8d32dfef24 fix thumbnailing animated webp 2023-05-03 04:01:34 +02:00
Flummi f39a136d5d improved clean script 2023-05-03 04:01:08 +02:00
Flummi 7dc03936ab select schmelect
fetch npm modules / f0ck the f0cker (push) Successful in 36s
2023-05-03 01:30:14 +02:00
Flummi 223c8893df thumbnailer trigger
fetch npm modules / f0ck the f0cker (push) Successful in 36s
2023-05-03 01:16:53 +02:00
Flummi 8e02892df0 buttons fix
fetch npm modules / f0ck the f0cker (push) Successful in 37s
2023-05-02 02:27:35 +02:00
Flummi 85d683ca31 Merge remote-tracking branch 'origin/dev'
fetch npm modules / f0ck the f0cker (push) Successful in 37s
2023-05-02 02:07:28 +02:00
Flummi 61e02953c7 tagger schmagger
fetch npm modules / f0ck the f0cker (push) Successful in 37s
2023-05-02 02:06:40 +02:00
Flummi 9ccf17ac54 tagkeyboard
fetch npm modules / f0ck the f0cker (push) Successful in 41s
2023-05-01 17:21:26 +02:00
Flummi 4e6b337d4f failsaphe
fetch npm modules / f0ck the f0cker (push) Successful in 40s
2023-05-01 15:51:41 +02:00
Flummi 9a55781a64 example jonguh 2023-05-01 15:50:33 +02:00
Flummi b95ce804ab extended mime check and failsafe
fetch npm modules / f0ck the f0cker (push) Successful in 44s
2023-05-01 15:44:40 +02:00
Flummi e0ac02e742 disable autotag sfw lol
fetch npm modules / f0ck the f0cker (push) Successful in 40s
2023-05-01 14:10:54 +02:00
Flummi d2bf6f7afd parser schmarser
fetch npm modules / f0ck the f0cker (push) Successful in 22s
2023-05-01 14:10:11 +02:00
Flummi d4218ad1e8 rip ci
fetch npm modules / f0ck the f0cker (push) Successful in 45s
2023-04-30 13:55:46 +02:00
Flummi bab2a03382 ci test
fetch npm modules / f0ck the f0cker (push) Failing after 1m13s
2023-04-30 13:53:34 +02:00
Flummi 4ca44ee8b9 -.-
fetch npm modules / f0ck the f0cker (push) Successful in 20s
2023-04-30 05:59:06 +02:00
Flummi 9157868699 test
fetch npm modules / f0ck the f0cker (push) Failing after 16s
2023-04-30 05:58:02 +02:00
Flummi dc986edfe4 forgot a colon lol
fetch npm modules / f0ck the f0cker (push) Failing after 0s
2023-04-30 05:49:28 +02:00
Flummi ac5c6ea0b5 CI runner with postgresql 2023-04-30 05:48:27 +02:00
Flummi 8ec51789dd CI test
fetch npm modules / Explore-Gitea-Actions (push) Successful in 33s
2023-04-30 05:23:59 +02:00
Flummi 8b6f68f2e9 misc.. 2023-04-28 07:04:06 +02:00
Flummi 44df4deea3 debug schmebug 2023-04-28 07:03:21 +02:00
Flummi b52b1bc19d update modules 2023-04-28 07:02:45 +02:00
kibi 7899f3bbd1 Merge pull request 'essential css tweak for paper theme' (#61) from dev into master
Reviewed-on: #61
2023-04-27 18:09:38 +00:00
kibi 955f844e16 essential css tweak for paper theme 2023-04-27 20:07:19 +02:00
kibi 3f0b502b00 Merge pull request 'fixing weird mini thumb in firefox' (#60) from dev into master
Reviewed-on: #60
2023-04-05 21:14:41 +00:00
kibi 5da67f2a6a fixing thumbnail 2023-04-05 23:14:23 +02:00
kibi bbe31594e5 fixing weird mini thumb in firefox 2023-04-05 23:08:08 +02:00
Flummi e16197f8e4 random(?) 2023-04-03 05:01:50 +02:00
Flummi 2186fc85d4 update modules 2023-04-03 05:00:59 +02:00
Flummi ca516ded7c revert cb0a5f7ee8
revert scroll content instead of body
2022-12-19 01:08:50 +00:00
kibi c50048d8e6 Merge pull request 'blah!' (#59) from dev into master
Reviewed-on: #59
2022-12-19 01:05:31 +00:00
schrumpel 49849a4a12 blah! 2022-12-19 02:04:11 +01:00
kibi 5e0ffda6c5 Merge pull request 'blah?' (#58) from dev into master
Reviewed-on: #58
2022-12-19 00:43:24 +00:00
schrumpel fdc8b836b3 blah? 2022-12-19 01:42:54 +01:00
kibi d37a55aa1e Merge pull request 'scroll content instead of body' (#57) from dev into master
Reviewed-on: #57
2022-12-18 19:57:17 +00:00
schrumpel cb0a5f7ee8 scroll content instead of body 2022-12-18 20:56:22 +01:00
schrumpel 0f6164a825 Merge remote-tracking branch 'origin/dev' 2022-10-27 18:00:40 +02:00
schrumpel 41e4bb8fae css changes 2022-10-27 18:00:34 +02:00
schrumpel ddaaaee502 css changes 2022-10-27 17:58:30 +02:00
kibi 534fb1d92a Merge pull request ':^)' (#55) from dev into master
Reviewed-on: #55
2022-10-27 17:32:55 +02:00
schrumpel cc5ddf27d3 :^) 2022-10-27 17:32:34 +02:00
kibi 6b6e44eed7 Merge pull request 'pagination responsiveness' (#54) from dev into master
Reviewed-on: #54
2022-10-27 15:27:27 +02:00
schrumpel e7f6ecd239 pagination responsiveness 2022-10-27 15:26:50 +02:00
kibi c806410be6 Merge pull request 'navbar rework' (#53) from dev into master
Reviewed-on: #53
2022-10-27 15:05:00 +02:00
schrumpel 2c9058dec7 navbar rework 2022-10-27 15:04:00 +02:00
kibi 9239666291 Merge pull request 'hallo flummer' (#51) from dev into master
Reviewed-on: #51
2022-10-23 01:48:20 +02:00
kibi d13eeea588 adding some more stuff for mobile navbar exception 2022-10-23 01:45:14 +02:00
kibi 79441cf69a Merge pull request 'master' (#50) from master into dev
Reviewed-on: #50
2022-10-23 01:40:03 +02:00
kibi 904a7abd86 Fixing the navbar on mobile devices 2022-10-23 01:22:19 +02:00
kibi aa0d88424b Merge pull request 'bye bye cuckflare' (#49) from dev into master
Reviewed-on: #49
2022-10-21 19:41:05 +02:00
kibi 201f7969b4 bye bye cuckflare 2022-10-21 19:38:20 +02:00
kibi 1da992d6a9 Fixing weird display bug in qutebrowser 2022-09-17 01:43:04 +02:00
Flummi 77a703a5f0 remove autotagger 2022-08-09 16:29:04 +02:00
Flummi dc14d2f78f Merge remote-tracking branch 'origin/dev' 2022-07-28 12:47:24 +02:00
Flummi 5fcc337038 Merge remote-tracking branch 'origin/dev' 2022-07-28 12:43:53 +02:00
Flummi 076e98a963 70 lol 2022-07-28 12:43:23 +02:00
Flummi daab816ad2 testpush lol 2022-06-07 17:12:50 +00:00
kibi 0b6b8c549a meer padding und soos 2022-05-28 08:44:27 +00:00
kibi 39ba04bcc9 meer padding und soos 2022-05-28 10:43:34 +02:00
Flummi 6b98458710 Merge remote-tracking branch 'origin/dev' 2022-05-25 15:26:53 +02:00
Flummi 729522369c Merge branch 'dev' of git.lat:f0ck/f0ckv2 into dev 2022-05-25 15:26:20 +02:00
Flummi 70f9e6553d don't include the username when showing favs 2022-05-25 15:25:36 +02:00
Flummi 2f7f8950ae Merge remote-tracking branch 'origin/dev' 2022-05-25 05:05:10 +02:00
Flummi f2ba18d186 fix spacing after tagging 2022-05-23 05:43:14 +00:00
Flummi 2309c3020a Merge remote-tracking branch 'origin/dev' 2022-05-22 16:44:46 +02:00
Flummi f8cc9f1dc0 xD 2022-05-22 14:42:01 +00:00
kibi 0754cb97a1 f0ck95 fix 2022-05-22 16:31:32 +02:00
Flummi 2c7a55415a very ugly recoversystem lul 2022-05-22 12:13:44 +00:00
kibi 1425361a6f Themes 2022-05-22 13:22:05 +02:00
Flummi f8c59111aa Merge remote-tracking branch 'origin/dev' 2022-05-22 11:18:57 +00:00
Flummi a9f65bb798 fix search 2022-05-22 11:18:34 +00:00
schrumpel d864679775 Merge remote-tracking branch 'origin/dev' 2022-05-22 11:07:35 +00:00
kibi e68af4dc96 f0ck95/95d fixes 2022-05-22 13:06:56 +02:00
Flummi 17c029e6da - added folder deleted to gitignore
- readme extended
2022-05-22 04:15:26 +00:00
Flummi 49fb4bfc56 Merge remote-tracking branch 'origin/dev' 2022-05-22 06:11:57 +02:00
Flummi c6fbe956a0 soft delete v1 2022-05-22 04:11:15 +00:00
Flummi 75c4e35fdc Merge remote-tracking branch 'origin/dev' 2022-05-21 17:04:36 +02:00
Flummi d2091494a1 fix tags + slugify 2022-05-21 15:03:01 +00:00
Flummi 53a0880c55 Merge remote-tracking branch 'origin/dev' 2022-05-21 16:43:27 +02:00
Flummi 74431a2a29 lel 2022-05-21 16:40:59 +02:00
Flummi af4c48f351 hide tags from the public 2022-05-21 16:39:24 +02:00
Flummi d0f8cb5acb fix pagination 2022-05-21 15:16:04 +02:00
Flummi 29d5b20f5b f0cklib2.0 2022-05-21 13:08:35 +00:00
kibi 86b13f5173 👀 ???? 2022-05-21 11:42:05 +02:00
kibi 5bb3fefe52 overhauling error page 2022-05-21 11:42:05 +02:00
kibi 41194ca08a 👀 ???? 2022-05-21 11:38:37 +02:00
kibi 314c476f8d overhauling error page 2022-05-21 11:34:44 +02:00
kibi a575f52005 overhauling error page 2022-05-21 11:30:23 +02:00
schrumpel 2ab187c780 Merge remote-tracking branch 'origin/dev' 2022-05-21 00:29:40 +00:00
kibi fa0496949c blub 2022-05-21 02:29:27 +02:00
schrumpel 4d93ef1152 Merge remote-tracking branch 'origin/dev' 2022-05-20 23:12:55 +00:00
kibi f8dffcdfc9 blah 2022-05-21 01:12:44 +02:00
schrumpel 3813af2c46 Merge remote-tracking branch 'origin/dev' 2022-05-20 23:10:03 +00:00
kibi 3ab534ee8e fix weird spacig for username 2022-05-21 01:09:49 +02:00
kibi 3e7ebba6fd avatar borders f0ck95 2022-05-21 01:01:17 +02:00
schrumpel 757c5aa4b4 Merge remote-tracking branch 'origin/dev' 2022-05-20 22:57:54 +00:00
kibi 7cc6776773 adding wrapper, fixing avatar width in nav 2022-05-21 00:57:31 +02:00
kibi 53ea6389c7 . 2022-05-19 16:17:44 +00:00
kibi edfdd7ebbe . 2022-05-19 18:17:06 +02:00
kibi 08698ee886 Merge pull request 'fixed navbar :)' (#47) from dev into master
Reviewed-on: #47
2022-05-19 15:31:54 +02:00
kibi e5b9bdb721 fixed navbar :) 2022-05-19 15:31:30 +02:00
kibi 961ec2cf55 Merge pull request 'blah' (#46) from dev into master
Reviewed-on: #46
2022-05-19 14:53:00 +02:00
kibi 8bee86c002 blah 2022-05-19 14:52:36 +02:00
kibi 9aec26f4d3 Merge pull request 'f0ck95 navlink unify with pagination style' (#45) from dev into master
Reviewed-on: #45
2022-05-19 14:46:38 +02:00
kibi e16138583e f0ck95 navlink unify with pagination style 2022-05-19 14:46:22 +02:00
schrumpel 10ef57b0bf Merge remote-tracking branch 'origin/dev' 2022-05-19 01:41:08 +00:00
kibi 9f4432a942 f0ck95 fixes 2022-05-19 03:40:20 +02:00
schrumpel b775d55fba Merge remote-tracking branch 'origin/dev' 2022-05-19 00:20:29 +00:00
kibi c33e57c9c0 badge word wrap for source field 2022-05-19 02:20:17 +02:00
schrumpel d315ae3efa Merge remote-tracking branch 'origin/dev' 2022-05-19 00:10:50 +00:00
kibi dc1d658c9a blah 2022-05-19 02:10:35 +02:00
schrumpel d4811c3d5c Merge remote-tracking branch 'origin/dev' 2022-05-19 00:04:57 +00:00
kibi 9be8b3f982 f0ck95 changes 2022-05-19 02:04:45 +02:00
schrumpel 1dacafdb3a Merge remote-tracking branch 'origin/dev' 2022-05-19 00:02:39 +00:00
kibi 3bc7cdcd1c f0ck95d black navbar brand bg 2022-05-19 02:02:24 +02:00
schrumpel dfea411e1b Merge remote-tracking branch 'origin/dev' 2022-05-19 00:00:52 +00:00
kibi 3a899e4911 cleaning up the css, bumping version an 2022-05-19 01:58:13 +02:00
kibi 7a96f26f84 possible sticky navbar fix for chrome 2022-05-19 01:52:15 +02:00
schrumpel 412a4a348a Merge remote-tracking branch 'origin/dev' 2022-05-18 20:26:52 +00:00
kibi 587bebbd6a mobile css fix 2022-05-18 20:26:25 +00:00
kibi 3aa14e4bfb hotfix xd 2022-05-18 20:26:25 +00:00
kibi 522d4903ba fix for image overlapping outside borders 2022-05-18 20:26:25 +00:00
Flummi 6d795652a7 fix 0px -> 0.. again 2022-05-18 20:26:25 +00:00
kibi 9c86d34b87 fixing image zoom 2022-05-18 20:26:25 +00:00
kibi 34e93b09c5 css fix 2022-05-18 20:26:25 +00:00
Flummi 48fd0e5ab0 fix indents.. as always
Signed-off-by: Flummi <git@srv.fail>
2022-05-18 20:26:25 +00:00
kibi 7df97485e6 borders morders 2022-05-18 20:26:25 +00:00
Flummi 8adee68835 hover schmover 2022-05-18 20:26:25 +00:00
kibi 0bda378993 smol border important 2022-05-18 20:26:25 +00:00
kibi b03d265a94 inset border for mystery numbers 2022-05-18 20:26:25 +00:00
kibi d78e90ddc0 f0ck95d changing sfw badge color to teal 2022-05-18 20:26:25 +00:00
kibi a0962d8d5c good executables have icons 2022-05-18 20:26:25 +00:00
kibi 58d8268207 removing italics 2022-05-18 20:26:25 +00:00
kibi 002b796cbc adding new variable for load indicator and footbar 2022-05-18 20:26:25 +00:00
Flummi d6561a66b8 bugfix 2022-05-18 20:26:25 +00:00
Flummi b82044555d #posts -> .posts 2022-05-18 20:26:25 +00:00
kibi 45792787ba only show admin tools when logged in 2022-05-18 20:26:25 +00:00
kibi 81f4817f03 adding new f0ck style, various css changes 2022-05-18 20:26:25 +00:00
Flummi e0618443c0 misc bugfixes 2022-05-18 20:26:25 +00:00
Flummi 6eddad7e0e userprofiles test 2022-05-18 20:26:25 +00:00
Flummi 6f71101b9e removed hardcoded link 2022-05-18 20:26:25 +00:00
Flummi a33d1606ae ability to color iconset from css 2022-05-18 20:26:25 +00:00
Flummi d7abc4e797 misc bugfixes 2022-05-18 20:26:25 +00:00
kibi 5bcd797e6c mobile css fix 2022-05-18 22:19:05 +02:00
kibi 530d0938c6 hotfix xd 2022-05-18 21:54:19 +02:00
kibi 7f5ef839cc fix for image overlapping outside borders 2022-05-18 21:50:01 +02:00
Flummi a106a31c47 fix 0px -> 0.. again 2022-05-18 19:08:00 +00:00
kibi 14c6816c47 fixing image zoom 2022-05-18 21:05:13 +02:00
kibi 643cf0de76 css fix 2022-05-18 20:37:48 +02:00
Flummi c18c6b9283 fix indents.. as always
Signed-off-by: Flummi <git@srv.fail>
2022-05-18 16:13:12 +00:00
kibi 4a7c7f8730 borders morders 2022-05-18 17:58:16 +02:00
Flummi 173321f5c8 hover schmover 2022-05-18 15:55:34 +00:00
kibi 6f12cbca32 smol border important 2022-05-18 17:27:30 +02:00
kibi a460b974a8 inset border for mystery numbers 2022-05-18 17:20:07 +02:00
kibi 09d647cc48 f0ck95d changing sfw badge color to teal 2022-05-18 17:14:43 +02:00
kibi d56c143e80 good executables have icons 2022-05-18 17:07:24 +02:00
kibi b9fbcb4187 removing italics 2022-05-18 16:51:53 +02:00
kibi 1f761ec62b adding new variable for load indicator and footbar 2022-05-18 16:45:14 +02:00
Flummi c0f8f6c536 bugfix 2022-05-18 12:12:25 +00:00
Flummi 881d44158e #posts -> .posts 2022-05-18 12:07:07 +00:00
kibi e159776e9b only show admin tools when logged in 2022-05-18 13:57:48 +02:00
kibi 44ca53a050 adding new f0ck style, various css changes 2022-05-18 13:37:26 +02:00
Flummi 97ef147160 misc bugfixes 2022-05-18 03:43:17 +00:00
Flummi f57a8b4320 userprofiles test 2022-05-17 14:57:32 +00:00
Flummi ba821b81fa removed hardcoded link 2022-05-17 10:59:17 +00:00
Flummi 29acafe918 ability to color iconset from css 2022-05-17 10:03:11 +00:00
Flummi dc96a2578a misc bugfixes 2022-05-17 09:40:43 +00:00
kibi 9fc920c1d2 Merge pull request 'making it more 95' (#44) from dev into master
Reviewed-on: #44
2022-05-17 11:19:01 +02:00
kibi eb16487d73 making it more 95 2022-05-17 11:18:49 +02:00
kibi 501f68c481 Merge pull request 'webkitscrollbar and darker badge color for f0ck95d' (#43) from dev into master
Reviewed-on: #43
2022-05-17 11:08:50 +02:00
kibi 4c94cac3ef webkitscrollbar and darker badge color for f0ck95d 2022-05-17 11:08:33 +02:00
kibi 5be4670ba3 Merge pull request 'hopefully last one for tonigh 👀' (#42) from dev into master
Reviewed-on: #42
2022-05-17 02:55:38 +02:00
kibi 50b983536c hopefully last one for tonigh 👀 2022-05-17 02:55:22 +02:00
kibi f0ee736e40 Merge pull request 'small color fix for f0ck95d' (#41) from dev into master
Reviewed-on: #41
2022-05-17 02:32:55 +02:00
kibi bb6f4dc2b8 small color fix for f0ck95d 2022-05-17 02:32:42 +02:00
kibi b960acb0ed Merge pull request '👀' (#40) from dev into master
Reviewed-on: #40
2022-05-17 02:28:34 +02:00
kibi 7621f6ca48 👀 2022-05-17 02:28:19 +02:00
kibi b8e69af878 Merge pull request '👀 f0ck95/f0ck95d fixes 👀' (#39) from dev into master
Reviewed-on: #39
2022-05-17 02:23:27 +02:00
kibi be64a42d68 👀 f0ck95/f0ck95d fixes 👀 2022-05-17 02:22:45 +02:00
kibi 3d35367302 Merge pull request 'adding f0ck95 dark' (#38) from dev into master
Reviewed-on: #38
2022-05-17 00:06:30 +02:00
kibi 80c7d2f3d7 adding f0ck95 dark 2022-05-17 00:05:37 +02:00
kibi 8dfee166b4 Merge pull request 'autism awareness for the placeholder' (#37) from dev into master
Reviewed-on: #37
2022-05-16 21:32:33 +02:00
kibi 7a007c95e7 autism awareness for the placeholder 2022-05-16 21:32:11 +02:00
kibi fd685a6c16 Merge pull request '👀 hopefully last hotfix for f0ck95' (#36) from dev into master
Reviewed-on: #36
2022-05-16 21:24:12 +02:00
kibi 75fa6e23f5 👀 hopefully last hotfix for f0ck95 2022-05-16 21:23:55 +02:00
kibi bafb0916e2 Merge pull request 'fixing all themes and removing opensans reference' (#35) from dev into master
Reviewed-on: #35
2022-05-16 20:35:38 +02:00
kibi fda30ebdee fixing all themes and removing opensans reference 2022-05-16 20:35:13 +02:00
kibi eb5d4ccfbe Merge pull request 'hotfix f0ck95' (#34) from dev into master
Reviewed-on: #34
2022-05-16 20:15:55 +02:00
kibi ce72c6f265 hotfix f0ck95 2022-05-16 20:15:40 +02:00
kibi cba529deb4 Merge pull request 'adding f0ck95 theme' (#33) from dev into master
Reviewed-on: #33
2022-05-16 20:08:15 +02:00
kibi 8dd58553e2 adding f0ck95 theme 2022-05-16 20:07:40 +02:00
kibi 8f4ea66cd9 Merge pull request 'hotfix for mobile dropdown alignment and display' (#32) from dev into master
Reviewed-on: #32
2022-05-16 16:19:21 +02:00
kibi 00837600bb hotfix for mobile dropdown alignment and display 2022-05-16 16:19:04 +02:00
kibi b76f6439fa Merge pull request 'hotfix for navbar dropdown fullwidth' (#31) from dev into master
Reviewed-on: #31
2022-05-16 16:10:56 +02:00
kibi 17946742ec hotfix for navbar dropdown fullwidth 2022-05-16 16:10:33 +02:00
kibi ae0d26aaa7 Merge pull request 'fix for fav tooltips and navigation spacing' (#30) from dev into master
Reviewed-on: #30
2022-05-16 16:03:07 +02:00
kibi 9d7b701e96 fix for fav tooltips and navigation spacing 2022-05-16 16:01:23 +02:00
Flummi 77c00de69a Merge remote-tracking branch 'origin/dev'
new file:   debug/adduser.mjs
modified:   f0ck.sql
	modified:   package.json
2022-05-16 12:58:52 +00:00
Flummi a7e2cb0dd9 psql-db: extension unaccent 2022-05-16 12:57:13 +00:00
Flummi 9100f64d81 adduser 2022-05-16 11:41:09 +00:00
Flummi 1222837d4d Merge remote-tracking branch 'origin/dev' 2022-05-16 12:42:03 +02:00
kibi 2447d62dac adding more in-depth python instructions 2022-05-16 12:39:42 +02:00
Flummi ac3647aa7f Merge remote-tracking branch 'origin/dev' 2022-05-14 19:23:02 +02:00
Flummi 429b5003d5 adminpanel layout fix 2022-05-14 19:22:35 +02:00
Flummi da5d7285a4 Merge remote-tracking branch 'origin/dev' 2022-05-14 14:41:11 +02:00
Flummi 1bc7085e68 autotagger is now userid: 1 2022-05-14 14:40:58 +02:00
Flummi f22b8b4e0d Merge remote-tracking branch 'origin/dev' 2022-05-14 13:47:43 +02:00
Flummi 617385b4b6 test -> f0ck xD 2022-05-14 13:47:33 +02:00
Flummi 1484c646e8 Merge remote-tracking branch 'origin/dev' 2022-05-14 13:33:20 +02:00
Flummi dd95c52de6 ... 2022-05-14 13:32:28 +02:00
Flummi f63556e713 readme.md 2022-05-14 13:30:12 +02:00
Flummi 16d85ae5ad Merge remote-tracking branch 'origin/dev' 2022-05-14 13:00:04 +02:00
Flummi 2ae986ccf1 enter message here 2022-05-14 12:59:35 +02:00
Flummi 4f5d54ee19 Merge remote-tracking branch 'origin/dev' 2022-05-12 16:18:35 +02:00
Flummi a97ed32c9f ability to delete own f0cks 2022-05-12 16:18:18 +02:00
Flummi 94c237561c Merge remote-tracking branch 'origin/dev' 2022-05-12 12:23:51 +02:00
Flummi 7e9599adce fixed navbar 2022-05-12 12:23:30 +02:00
Flummi 9dfffbb8bf Merge remote-tracking branch 'origin/dev' 2022-05-09 16:43:49 +02:00
Flummi b0acc1731d oopsie 2022-05-09 16:43:39 +02:00
Flummi 647991b5e9 Merge remote-tracking branch 'origin/dev' 2022-05-09 16:40:52 +02:00
Flummi 78b847ddae more nsfw etzala 2022-05-09 16:40:35 +02:00
Flummi 1a3af07309 Merge remote-tracking branch 'origin/dev' 2022-05-09 13:00:29 +02:00
Flummi 8f5680e50f keybinds - prevent triggering when holding shift, alt, ctrl or meta key 2022-05-09 12:59:33 +02:00
Flummi add3c7a648 Merge remote-tracking branch 'origin/dev' 2022-05-08 05:51:43 +02:00
Flummi e7ee063021 psql search_path 2022-05-08 05:51:09 +02:00
kibi d57908f82e Merge pull request 'adding search description for mobile view and small admin page restructuring' (#29) from dev into master
Reviewed-on: #29
2022-05-08 01:16:48 +02:00
kibi 659adca258 making the admin page a bit better 2022-05-08 01:14:43 +02:00
kibi 46d8bd45a8 adding search description for mobile view 2022-05-08 01:14:13 +02:00
kibi 3731d1d785 adding search description for mobile view 2022-05-08 01:13:23 +02:00
Flummi 872abd7f73 cfg: change psql schema 2022-05-07 19:08:55 +02:00
Flummi 2a94a84c44 Merge remote-tracking branch 'origin/dev' 2022-05-07 17:49:51 +02:00
kibi 1787a69143 hopefully foolproof 2022-05-07 17:40:39 +02:00
kibi ac96827ad1 b/.empty 2022-05-07 17:39:13 +02:00
kibi 2e0881b109 undo 2022-05-07 17:35:43 +02:00
kibi 315770bf30 lul revert 2022-05-07 16:17:38 +02:00
kibi 6de804561f lul 2022-05-07 16:15:32 +02:00
Flummi f4815d95e5 Merge remote-tracking branch 'origin/dev' 2022-05-07 14:09:41 +02:00
Flummi 7e1129a567 xd 2022-05-07 14:09:21 +02:00
Flummi 1e01a521e4 Merge remote-tracking branch 'origin/dev' 2022-05-07 12:38:23 +02:00
Flummi 9541d9e0b8 update cuffeo 2022-05-07 12:36:41 +02:00
Flummi fb744fe341 Merge pull request 'fuuuuuuuuuck' (#28) from dev into master
Reviewed-on: #28
2022-05-07 10:45:51 +02:00
Flummi a6b881a196 fuuuuuuuuuck 2022-05-07 10:45:32 +02:00
Flummi 1be456edb4 Merge pull request 'e.photo -> e.media' (#27) from dev into master
Reviewed-on: #27
2022-05-07 10:40:50 +02:00
Flummi 1066e8772b e.photo -> e.media 2022-05-07 10:40:37 +02:00
Flummi 4ebcbdd886 Merge pull request ':_D' (#26) from dev into master
Reviewed-on: #26
2022-05-07 10:37:13 +02:00
Flummi 73577441d1 :_D 2022-05-07 10:36:58 +02:00
Flummi 1a8aa81a0b Merge pull request 'dev' (#25) from dev into master
Reviewed-on: #25
2022-05-07 10:30:38 +02:00
Flummi 86409c9d98 telegram magic 2022-05-07 10:29:53 +02:00
Flummi 22b171c858 emphasis on active id in pagination 2022-05-07 10:21:32 +02:00
Flummi 155d496592 fix random & search 2022-05-06 17:29:55 +02:00
Flummi 82702d786a Merge pull request 'fix random & search' (#24) from dev into master
Reviewed-on: #24
2022-05-06 17:29:53 +02:00
63 changed files with 4193 additions and 1426 deletions
+43
View File
@@ -0,0 +1,43 @@
name: fetch npm modules
run-name: fetch npm modules
on: [push]
jobs:
f0ck the f0cker:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15.2
env:
POSTGRES_USER: f0ck
POSTGRES_PASSWORD: f0ck
POSTGRES_DB: f0ck
POSTGRES_PORT: 5432
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 19
- run: npm ci
- name: Install PostgreSQL client
run: |
apt-get update
apt-get install --yes postgresql-client
- name: import f0ck database
run: psql -h postgres -d f0ck -U f0ck < f0ck.sql
env:
PGPASSWORD: f0ck
- name: create directories
run: mkdir -p tmp public/ca deleted/{ca,b,t}
- name: copy config
run: cp config_example.json config.json
+6 -3
View File
@@ -1,7 +1,10 @@
node_modules/ node_modules/
logs/*.log logs/*.log
config.json config.json
public/b/* public/b
public/ca/* public/ca
public/t/* public/t
deleted/b
deleted/ca
deleted/t
tmp/* tmp/*
+30
View File
@@ -0,0 +1,30 @@
# how to install:
## dependencies
```bash
sudo pacman -S nodejs npm ffmpeg yt-dlp ffmpegthumbnailer postgresql imagemagick git mime-types
```
## postgres
```bash
sudo -u postgres initdb --locale en_US.UTF-8 -D '/var/lib/postgres/data'
#(if it fails: sudo localectl set-locale en_US.UTF-8)
sudo systemctl enable --now postgresql
#(if you're retarded or lazy, append postgresql and postgresql-libs to your ignorepkg)
sudo -u postgres createuser -S -D -R -e f0ck
sudo -u postgres createdb f0ck -O f0ck
```
## install f0ck
```bash
sudo useradd f0ck -m
sudo -iu f0ck
cd ~
git clone https://git.lat/f0ck/f0ckv2.git
cd f0ckv2
#(for developers: git checkout dev)
npm i
psql f0ck < f0ck.sql
mkdir -p public/ca deleted/{ca,b,t}
cp config_example.json config.json
- edit config.json
- set up clients, as described here: https://git.lat/keinBot/cuffeo
npm start
```
+76
View File
@@ -0,0 +1,76 @@
{
"main": {
"url": {
"full": "https://f0ck.dev",
"domain": "f0ck.dev",
"regex": "f0ck\\.dev"
},
"maxfilesize": 83886080,
"adminmultiplier": 3.5,
"ignored": [
"f0ck.dev",
"f0ck.me"
]
},
"allowedModes": [ "sfw", "nsfw", "untagged", "all" ],
"allowedMimes": [ "audio", "image", "video" ],
"nsfp": [],
"websrv": {
"port": "8080",
"paths": {
"images": "/b",
"thumbnails": "/t",
"coverarts": "/ca"
},
"themes": [ "f0ck", "p1nk", "orange", "atmos", "amoled", "paper", "term", "iced", "f0ck95", "f0ck95d" ],
"eps": 300,
"cache": false,
"phrases": [
"<img src=\"/s/img/crap/nyancat.gif\" style=\"margin-top: 5px\" />"
]
},
"clients": [{
"type": "irc",
"enabled": true,
"network": "n0xy",
"host": "irc.n0xy.net",
"port": 6697,
"ssl": true,
"selfSigned": false,
"sasl": false,
"nickname": "f0ckci",
"username": "f0ckci",
"realname": "f0ckci",
"channels": [
"#f0ck-dev"
]
}],
"sql": {
"host": "localhost",
"user": "f0ck",
"password": "f0ck",
"database": "f0ck",
"schema": "public",
"multipleStatements": true
},
"admins": [
],
"mimes": {
"image/png": "png",
"video/webm": "webm",
"image/gif": "gif",
"image/jpeg": "jpg",
"image/webp": "webp",
"video/mp4": "mp4",
"video/quicktime": "mp4",
"audio/mpeg": "mpg",
"audio/mp3": "mp3",
"audio/ogg": "ogg",
"audio/opus": "opus",
"audio/flac": "flac",
"audio/x-flac": "flac",
"video/x-m4v": "mp4",
"video/x-matroska": "mkv"
}
}
+3 -3
View File
@@ -36,7 +36,7 @@ import lib from "../src/inc/lib.mjs";
db({ db({
item_id: f.id, item_id: f.id,
tag_id: tmp.nsfw ? 2 : 1, tag_id: tmp.nsfw ? 2 : 1,
user_id: 7 user_id: 1
}) })
} }
`; `;
@@ -46,8 +46,8 @@ import lib from "../src/inc/lib.mjs";
insert into "tags_assign" ${ insert into "tags_assign" ${
db({ db({
item_id: f.id, item_id: f.id,
tag_id: 8, // hentai tag_id: 4, // hentai
user_id: 7 // autotagger user_id: 1 // autotagger
}) })
} }
`; `;
+86 -24
View File
@@ -1,33 +1,95 @@
import sql from "../src/inc/sql.mjs"; import cfg from "../src/inc/config.mjs";
import { promises as fs } from "fs"; import db from "../src/inc/sql.mjs";
import fs from "node:fs";
import readline from 'node:readline/promises';
const opts = { const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
b: "public/b",
t: "public/t",
tmp: "tmp"
};
const count = {
b: 0, t: 0, tmp: 0
};
const rows = await sql('items').select('id', 'dest'); // npm run clean -- --dry-run
const ids = rows.map(r => r.id.toString() + ".webp"); const dry = !!process.argv.filter(a => a == '--dry-run').length;
const dests = rows.map(r => r.dest); console.log(`dry run? ${dry}`);
const dirs = {
b: "./public/b",
t: "./public/t",
tmp: "./tmp"
};
const files = { const files = {
b: (await fs.readdir(opts.b)).filter(f => f !== '.empty'), b: (await fs.promises.readdir(dirs.b)) .filter(f => f !== '.empty'),
t: await fs.readdir(opts.t) t: (await fs.promises.readdir(dirs.t)) .filter(f => f !== '.empty'),
tmp: (await fs.promises.readdir(dirs.tmp)).filter(f => f !== '.empty')
};
const extensions = [ ...Object.values(cfg.mimes), 'mov' ];
const count = {
missing: { b: [], t: [] },
invalid: { b: [], t: [] },
spare: { b: [], t: [] },
tmp: files.tmp
};
const rows = await db`select id, dest from items where active = true`;
const f0cks = {
b: rows.flatMap(f => f.dest),
t: rows.flatMap(f => `${f.id}.webp`)
}; };
const unused = { // missing
b: files.b.filter(f => !dests.includes(f)), for(const row of rows) {
t: files.t.filter(f => !ids.includes(f)) if(!fs.existsSync(`${dirs.b}/${row.dest}`))
count.missing.b.push(row.id);
if(!fs.existsSync(`${dirs.t}/${row.id}.webp`))
count.missing.t.push(row.id);
} }
count.b = (await Promise.all(unused.b.map(f => fs.rm(`${opts.b}/${f}`)))).length; // invalid
count.t = (await Promise.all(unused.t.map(f => fs.rm(`${opts.t}/${f}`)))).length; count.invalid.b = files.b.filter(f => !extensions.includes(f.toLowerCase().split('.')[1]));
count.invalid.t = files.t.filter(f => !f.endsWith('.webp'));
// clear tmp // spare
const tmp = (await fs.readdir(opts.tmp)).filter(f => f !== '.empty'); for(const file of files.b)
count.tmp = (await Promise.all(tmp.map(f => fs.rm(`${opts.tmp}/${f}`)))).length; if(!f0cks.b.includes(file))
count.spare.b.push(`${dirs.b}/${file}`);
console.log(count, unused); for(const file of files.t)
if(!f0cks.t.includes(file))
count.spare.t.push(`${dirs.t}/${file}`);
// show confusing summary
console.log(count);
// delete spare if --dry-run
if(!dry) {
let q;
if(count.spare.b.length > 0) {
q = (await rl.question(`delete ${count.spare.b.length} unnecessary files in ${dirs.b}? [y/N] `)) == 'y';
if(q) {
await Promise.all(count.spare.b.map(f => fs.promises.unlink(f)));
console.log(`deleted ${count.spare.b.length} files`);
}
else
console.log('abort...');
}
if(count.spare.t.length > 0) {
q = (await rl.question(`delete ${count.spare.t.length} unnecessary files in ${dirs.t}? [y/N] `)) == 'y';
if(q) {
await Promise.all(count.spare.t.map(f => fs.promises.unlink(f)));
console.log(`deleted ${count.spare.t.length} files`);
}
else
console.log('abort...');
}
if(files.tmp.length > 0) {
q = (await rl.question(`delete ${files.tmp.length} files in ${dirs.tmp}? [y/N] `)) == 'y';
if(q) {
await Promise.all(files.tmp.map(f => fs.promises.unlink(`${dirs.tmp}/${f}`)));
console.log(`deleted ${files.tmp.length} files`);
}
else
console.log('abort...');
}
}
// close connection
await db.end();
process.exit();
+215
View File
@@ -0,0 +1,215 @@
\connect "f0ck";
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
CREATE EXTENSION unaccent;
CREATE FUNCTION public.delete_unused_tags() RETURNS trigger
LANGUAGE plpgsql
AS $$
begin
delete from tags
where
tags.id not in (select tag_id from tags_assign) and
tags.id = OLD.tag_id and
tags.tag != 'sfw' and
tags.tag != 'nsfw' and
tags.tag != 'hentai' and
tags.tag != 'audio';
return OLD;
end $$;
ALTER FUNCTION public.delete_unused_tags() OWNER TO f0ck;
CREATE FUNCTION public.fill_normalized() RETURNS trigger
LANGUAGE plpgsql
AS $$
begin
NEW.normalized = slugify(NEW.tag);
return NEW;
end$$;
ALTER FUNCTION public.fill_normalized() OWNER TO f0ck;
CREATE FUNCTION public.slugify(v text) RETURNS text
LANGUAGE plpgsql
AS $$
BEGIN
RETURN trim(BOTH '-' FROM regexp_replace(lower(unaccent(trim(v))), '[\u0000-\u002f \u003a-\u0040\u005b-\u0060\u007b-\u00bf]+', '', 'gi'));
END;
$$;
ALTER FUNCTION public.slugify(v text) OWNER TO f0ck;
CREATE TABLE public.favorites (
user_id integer NOT NULL,
item_id integer NOT NULL
);
ALTER TABLE public.favorites OWNER TO f0ck;
CREATE SEQUENCE public.items_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.items_id_seq OWNER TO f0ck;
CREATE TABLE public.items (
id integer DEFAULT nextval('public.items_id_seq'::regclass) NOT NULL,
src character varying(255) NOT NULL,
dest character varying(40) NOT NULL,
mime character varying(100) NOT NULL,
size integer NOT NULL,
checksum character varying(255) NOT NULL,
username character varying(40) NOT NULL,
userchannel character varying(100) NOT NULL,
usernetwork character varying(40) NOT NULL,
stamp integer NOT NULL,
active boolean NOT NULL,
thumb character varying(100)
);
ALTER TABLE public.items OWNER TO f0ck;
COMMENT ON COLUMN public.items.src IS 'src-Link';
COMMENT ON COLUMN public.items.dest IS 'filename';
CREATE SEQUENCE public.tags_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.tags_id_seq OWNER TO f0ck;
CREATE TABLE public.tags (
id integer DEFAULT nextval('public.tags_id_seq'::regclass) NOT NULL,
tag character varying(45) NOT NULL,
normalized character varying(45) NOT NULL
);
ALTER TABLE public.tags OWNER TO f0ck;
CREATE TABLE public.tags_alias (
tag_orig_id integer NOT NULL,
tag_alias character varying NOT NULL
);
ALTER TABLE public.tags_alias OWNER TO f0ck;
CREATE TABLE public.tags_assign (
item_id integer NOT NULL,
tag_id integer NOT NULL,
user_id integer DEFAULT 2 NOT NULL
);
ALTER TABLE public.tags_assign OWNER TO f0ck;
CREATE SEQUENCE public.user_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.user_id_seq OWNER TO f0ck;
CREATE TABLE public."user" (
id integer DEFAULT nextval('public.user_id_seq'::regclass) NOT NULL,
login character varying(255) NOT NULL,
"user" character varying(255) NOT NULL,
password character varying(167) NOT NULL,
level integer NOT NULL
);
ALTER TABLE public."user" OWNER TO f0ck;
CREATE TABLE public.user_options (
user_id integer NOT NULL,
mode integer NOT NULL,
theme character varying(50) NOT NULL,
avatar integer DEFAULT 1 NOT NULL,
fullscreen integer DEFAULT 0 NOT NULL
);
ALTER TABLE public.user_options OWNER TO f0ck;
CREATE SEQUENCE public.user_sessions_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
ALTER TABLE public.user_sessions_id_seq OWNER TO f0ck;
CREATE TABLE public.user_sessions (
id integer DEFAULT nextval('public.user_sessions_id_seq'::regclass) NOT NULL,
user_id integer NOT NULL,
session character varying(32) NOT NULL,
browser character varying(255) NOT NULL,
created_at integer NOT NULL,
last_used integer NOT NULL,
last_action character varying(255) NOT NULL,
kmsi smallint DEFAULT '0'::smallint NOT NULL
);
ALTER TABLE public.user_sessions OWNER TO f0ck;
COPY public.items (id, src, dest, mime, size, checksum, username, userchannel, usernetwork, stamp, active, thumb) FROM stdin;
1 b761fa9339.png b761fa9339.png image/png 164 keinPlan Flummi #f0ck n0xy 1471250800 t
\.
COPY public.tags (id, tag, normalized) FROM stdin;
1 sfw sfw
2 nsfw nsfw
3 audio audio
4 hentai hentai
\.
COPY public.tags_assign (item_id, tag_id, user_id) FROM stdin;
1 1 1
\.
COPY public."user" (id, login, "user", password, level) FROM stdin;
1 autotagger autotagger f0ck you 0
2 deleted deleted f0ck you 0
\.
SELECT pg_catalog.setval('public.items_id_seq', 2, true);
SELECT pg_catalog.setval('public.tags_id_seq', 5, true);
SELECT pg_catalog.setval('public.user_id_seq', 3, true);
SELECT pg_catalog.setval('public.user_sessions_id_seq', 1, true);
ALTER TABLE ONLY public.favorites ADD CONSTRAINT idx_16521_primary PRIMARY KEY (user_id, item_id);
ALTER TABLE ONLY public.items ADD CONSTRAINT idx_16526_primary PRIMARY KEY (id);
ALTER TABLE ONLY public."user" ADD CONSTRAINT idx_16554_primary PRIMARY KEY (id);
ALTER TABLE ONLY public.user_options ADD CONSTRAINT idx_16567_user_id UNIQUE (user_id);
ALTER TABLE ONLY public.user_sessions ADD CONSTRAINT idx_16572_primary PRIMARY KEY (id);
ALTER TABLE ONLY public.items ADD CONSTRAINT items_checksum UNIQUE (checksum);
ALTER TABLE ONLY public.tags_alias ADD CONSTRAINT tags_alias_tag_alias_tag_orig_id UNIQUE (tag_alias, tag_orig_id);
ALTER TABLE ONLY public.tags_alias ADD CONSTRAINT tags_alias_tag_orig_id PRIMARY KEY (tag_orig_id);
ALTER TABLE ONLY public.tags_assign ADD CONSTRAINT tags_assign_item_id_tag_id_primary PRIMARY KEY (item_id, tag_id);
ALTER TABLE ONLY public.tags_assign ADD CONSTRAINT tags_assign_item_id_tag_id_unique UNIQUE (item_id, tag_id);
ALTER TABLE ONLY public.tags ADD CONSTRAINT tags_id PRIMARY KEY (id);
ALTER TABLE ONLY public.tags ADD CONSTRAINT tags_normalized UNIQUE (normalized);
ALTER TABLE ONLY public.tags ADD CONSTRAINT tags_tag UNIQUE (tag);
ALTER TABLE ONLY public.user_options ADD CONSTRAINT user_options_user_id PRIMARY KEY (user_id);
CREATE TRIGGER tags_assign_ad AFTER DELETE ON public.tags_assign FOR EACH ROW EXECUTE FUNCTION public.delete_unused_tags();
CREATE TRIGGER tags_bi BEFORE INSERT ON public.tags FOR EACH ROW EXECUTE FUNCTION public.fill_normalized();
CREATE TRIGGER tags_bu BEFORE UPDATE ON public.tags FOR EACH ROW EXECUTE FUNCTION public.fill_normalized();
ALTER TABLE ONLY public.favorites ADD CONSTRAINT favorites_item_id_fkey FOREIGN KEY (item_id) REFERENCES public.items(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.favorites ADD CONSTRAINT favorites_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.tags_alias ADD CONSTRAINT tags_alias_tag_orig_id_fkey FOREIGN KEY (tag_orig_id) REFERENCES public.tags(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.tags_assign ADD CONSTRAINT tags_assign_item_id_fkey FOREIGN KEY (item_id) REFERENCES public.items(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.tags_assign ADD CONSTRAINT tags_assign_tag_id_fkey FOREIGN KEY (tag_id) REFERENCES public.tags(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.tags_assign ADD CONSTRAINT tags_assign_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE SET DEFAULT;
ALTER TABLE ONLY public.user_options ADD CONSTRAINT user_options_avatar_fkey FOREIGN KEY (avatar) REFERENCES public.items(id) ON DELETE SET DEFAULT;
ALTER TABLE ONLY public.user_options ADD CONSTRAINT user_options_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.user_sessions ADD CONSTRAINT user_sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE;
BIN
View File
Binary file not shown.
+18 -37
View File
@@ -1,26 +1,26 @@
{ {
"name": "f0ckv2", "name": "f0ckv2",
"version": "2.2.0", "version": "2.2.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "f0ckv2", "name": "f0ckv2",
"version": "2.2.0", "version": "2.2.1",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cuffeo": "^1.0.7-3", "cuffeo": "^1.2.2",
"flumm-fetch": "^1.0.1", "flumm-fetch": "^1.0.1",
"flummpress": "^2.0.5", "flummpress": "^2.0.5",
"postgres": "^3.0.1" "postgres": "^3.3.4"
} }
}, },
"node_modules/cuffeo": { "node_modules/cuffeo": {
"version": "1.0.7-3", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.0.7-3.tgz", "resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.2.2.tgz",
"integrity": "sha512-Lz8AlLdFWeLLGsf6KBXTHnpseeMbuQH69BamhZr8O9Se9pzuHQgv/ed13n2XWreN0RTyRa49YPtsNVwoQnNrLw==", "integrity": "sha512-Pd2AL/Zp5RCAbaSbT7rLUOyxSEVCFovihaIaje7uYzpQMzIwPbFapQ/mn2d2iz+Tbkjs9LF+FD5JzAvANh9Wzw==",
"dependencies": { "dependencies": {
"flumm-fetch-cookies": "^1.4.0" "flumm-fetch": "^1.0.1"
} }
}, },
"node_modules/flumm-fetch": { "node_modules/flumm-fetch": {
@@ -28,26 +28,15 @@
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz", "resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
"integrity": "sha512-pZ5U0hheCSW43vfGZQMunr03U7rUOX+iy2y13Tu4nc3iRL+E/Qfeo5nZ2B2JMYKOGIx1A1anUYOz+ulyhouyjg==" "integrity": "sha512-pZ5U0hheCSW43vfGZQMunr03U7rUOX+iy2y13Tu4nc3iRL+E/Qfeo5nZ2B2JMYKOGIx1A1anUYOz+ulyhouyjg=="
}, },
"node_modules/flumm-fetch-cookies": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/flumm-fetch-cookies/-/flumm-fetch-cookies-1.4.0.tgz",
"integrity": "sha512-OHGUak5iHl9GoDdbkAMsrL9ONkFQ8opd0jYQ9lMUuGVqIP+JPbyzQqElhpwJxLaV/WSE4LR/2dnHoguFHFSLFA==",
"dependencies": {
"flumm-fetch": "^1.0.1"
},
"engines": {
"node": ">=11.14.0"
}
},
"node_modules/flummpress": { "node_modules/flummpress": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz", "resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz",
"integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ==" "integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ=="
}, },
"node_modules/postgres": { "node_modules/postgres": {
"version": "3.1.0", "version": "3.3.4",
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.1.0.tgz", "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz",
"integrity": "sha512-yQbJtA7z6SsQuFiF01rmHlkG2gU5IEv9D/Pn2fu8Tz6x3/suNWZs4fIPd59rkL+dfVfhDqYQ7DU3YnUqzEDB5Q==", "integrity": "sha512-XVu0+d/Y56pl2lSaf0c7V19AhAEfpVrhID1IENWN8nf0xch6hFq6dAov5dtUX6ZD46wfr1TxvLhxLtV8WnNsOg==",
"funding": { "funding": {
"type": "individual", "type": "individual",
"url": "https://github.com/sponsors/porsager" "url": "https://github.com/sponsors/porsager"
@@ -56,11 +45,11 @@
}, },
"dependencies": { "dependencies": {
"cuffeo": { "cuffeo": {
"version": "1.0.7-3", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.0.7-3.tgz", "resolved": "https://registry.npmjs.org/cuffeo/-/cuffeo-1.2.2.tgz",
"integrity": "sha512-Lz8AlLdFWeLLGsf6KBXTHnpseeMbuQH69BamhZr8O9Se9pzuHQgv/ed13n2XWreN0RTyRa49YPtsNVwoQnNrLw==", "integrity": "sha512-Pd2AL/Zp5RCAbaSbT7rLUOyxSEVCFovihaIaje7uYzpQMzIwPbFapQ/mn2d2iz+Tbkjs9LF+FD5JzAvANh9Wzw==",
"requires": { "requires": {
"flumm-fetch-cookies": "^1.4.0" "flumm-fetch": "^1.0.1"
} }
}, },
"flumm-fetch": { "flumm-fetch": {
@@ -68,23 +57,15 @@
"resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz", "resolved": "https://registry.npmjs.org/flumm-fetch/-/flumm-fetch-1.0.1.tgz",
"integrity": "sha512-pZ5U0hheCSW43vfGZQMunr03U7rUOX+iy2y13Tu4nc3iRL+E/Qfeo5nZ2B2JMYKOGIx1A1anUYOz+ulyhouyjg==" "integrity": "sha512-pZ5U0hheCSW43vfGZQMunr03U7rUOX+iy2y13Tu4nc3iRL+E/Qfeo5nZ2B2JMYKOGIx1A1anUYOz+ulyhouyjg=="
}, },
"flumm-fetch-cookies": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/flumm-fetch-cookies/-/flumm-fetch-cookies-1.4.0.tgz",
"integrity": "sha512-OHGUak5iHl9GoDdbkAMsrL9ONkFQ8opd0jYQ9lMUuGVqIP+JPbyzQqElhpwJxLaV/WSE4LR/2dnHoguFHFSLFA==",
"requires": {
"flumm-fetch": "^1.0.1"
}
},
"flummpress": { "flummpress": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz", "resolved": "https://registry.npmjs.org/flummpress/-/flummpress-2.0.5.tgz",
"integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ==" "integrity": "sha512-C/8Im6OvoZw67q9DvYIXKjKr28zHYLJdH4DucQ6zpVbN1eWPySmxkJTURbkq3uEwABXLngXLifS6mjxAC++umQ=="
}, },
"postgres": { "postgres": {
"version": "3.1.0", "version": "3.3.4",
"resolved": "https://registry.npmjs.org/postgres/-/postgres-3.1.0.tgz", "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.3.4.tgz",
"integrity": "sha512-yQbJtA7z6SsQuFiF01rmHlkG2gU5IEv9D/Pn2fu8Tz6x3/suNWZs4fIPd59rkL+dfVfhDqYQ7DU3YnUqzEDB5Q==" "integrity": "sha512-XVu0+d/Y56pl2lSaf0c7V19AhAEfpVrhID1IENWN8nf0xch6hFq6dAov5dtUX6ZD46wfr1TxvLhxLtV8WnNsOg=="
} }
} }
} }
+10 -9
View File
@@ -1,22 +1,23 @@
{ {
"name": "f0ckv2", "name": "f0ckv2",
"version": "2.2.0", "version": "2.2.1",
"description": "f0ck, kennste?", "description": "f0ck, kennste?",
"main": "index.mjs", "main": "index.mjs",
"type": "module",
"scripts": { "scripts": {
"start": "node --experimental-json-modules src/index.mjs", "start": "node --trace-uncaught src/index.mjs",
"trigger": "node --experimental-json-modules debug/trigger.mjs", "trigger": "node debug/trigger.mjs",
"autotagger": "node --experimental-json-modules debug/autotagger.mjs", "autotagger": "node debug/autotagger.mjs",
"thumbnailer": "node --experimental-json-modules debug/thumbnailer.mjs", "thumbnailer": "node debug/thumbnailer.mjs",
"test": "node --experimental-json-modules debug/test.mjs", "test": "node debug/test.mjs",
"clean": "node --experimental-json-modules debug/clean.mjs" "clean": "node debug/clean.mjs"
}, },
"author": "Flummi", "author": "Flummi",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cuffeo": "^1.0.7-3", "cuffeo": "^1.2.2",
"flumm-fetch": "^1.0.1", "flumm-fetch": "^1.0.1",
"flummpress": "^2.0.5", "flummpress": "^2.0.5",
"postgres": "^3.0.1" "postgres": "^3.3.4"
} }
} }
Executable → Regular
View File
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

+1345 -142
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -72,7 +72,7 @@
display: none; display: none;
} }
svg { .v0ck_player_controls svg {
width: 20px; width: 20px;
height: 20px; height: 20px;
fill: #fff; fill: #fff;
+5 -3
View File
@@ -3,8 +3,10 @@
<svg xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg">
<!-- Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --> <!-- Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
<defs> <defs>
<symbol style="fill: var(--accent)" id="heart_regular" viewBox="0 0 512 512"><path d="M458.4 64.3C400.6 15.7 311.3 23 256 79.3 200.7 23 111.4 15.6 53.6 64.3-21.6 127.6-10.6 230.8 43 285.5l175.4 178.7c10 10.2 23.4 15.9 37.6 15.9 14.3 0 27.6-5.6 37.6-15.8L469 285.6c53.5-54.7 64.7-157.9-10.6-221.3zm-23.6 187.5L259.4 430.5c-2.4 2.4-4.4 2.4-6.8 0L77.2 251.8c-36.5-37.2-43.9-107.6 7.3-150.7 38.9-32.7 98.9-27.8 136.5 10.5l35 35.7 35-35.7c37.8-38.5 97.8-43.2 136.5-10.6 51.1 43.1 43.5 113.9 7.3 150.8z"/></symbol> <symbol id="heart_regular" viewBox="0 0 512 512"><path d="M458.4 64.3C400.6 15.7 311.3 23 256 79.3 200.7 23 111.4 15.6 53.6 64.3-21.6 127.6-10.6 230.8 43 285.5l175.4 178.7c10 10.2 23.4 15.9 37.6 15.9 14.3 0 27.6-5.6 37.6-15.8L469 285.6c53.5-54.7 64.7-157.9-10.6-221.3zm-23.6 187.5L259.4 430.5c-2.4 2.4-4.4 2.4-6.8 0L77.2 251.8c-36.5-37.2-43.9-107.6 7.3-150.7 38.9-32.7 98.9-27.8 136.5 10.5l35 35.7 35-35.7c37.8-38.5 97.8-43.2 136.5-10.6 51.1 43.1 43.5 113.9 7.3 150.8z"/></symbol>
<symbol style="fill: var(--accent)" id="heart_solid" viewBox="0 0 512 512"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z"/></symbol> <symbol id="heart_solid" viewBox="0 0 512 512"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z"/></symbol>
<symbol id="cross" viewBox="0 0 512 512"><path d="M53.6,62.3 L458.4,458.4 M458.4,62.3 L53.6,458.4" style="stroke-linecap: round;stroke-width: 60;stroke: var(--accent);"/></symbol> <symbol id="cross" viewBox="0 0 512 512"><path d="M53.6,62.3 L458.4,458.4 M458.4,62.3 L53.6,458.4" style="stroke-linecap: round;stroke-width: 60;"/></symbol>
<symbol id="window-maximize" viewBox="0 0 512 512"><path d="M464 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h416c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zm0 394c0 3.3-2.7 6-6 6H54c-3.3 0-6-2.7-6-6V192h416v234z" style="fill: var(--maximize_button);"/></symbol>
<symbol id="window-minimize" viewBox="0 0 512 512"><path d="M464 0H144c-26.5 0-48 21.5-48 48v48H48c-26.5 0-48 21.5-48 48v320c0 26.5 21.5 48 48 48h320c26.5 0 48-21.5 48-48v-48h48c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48zm-96 464H48V256h320v208zm96-96h-48V144c0-26.5-21.5-48-48-48H144V48h320v320z" style="fill: var(--maximize_button);"/></symbol>
</defs> </defs>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

+4 -2
View File
@@ -85,7 +85,7 @@ const flash = ({ type, msg }) => {
[...document.querySelectorAll("#tags > .badge")].forEach(tag => tag.parentElement.removeChild(tag)); [...document.querySelectorAll("#tags > .badge")].forEach(tag => tag.parentElement.removeChild(tag));
_tags.reverse().forEach(tag => { _tags.reverse().forEach(tag => {
const a = document.createElement("a"); const a = document.createElement("a");
a.href = `/tag/${tag.tag}`; a.href = `/tag/${tag.normalized}`;
a.style = "color: inherit !important"; a.style = "color: inherit !important";
a.innerHTML = tag.tag; a.innerHTML = tag.tag;
a.addEventListener("click", editTagEvent); // tmp a.addEventListener("click", editTagEvent); // tmp
@@ -101,6 +101,7 @@ const flash = ({ type, msg }) => {
delbutton.href = "#"; delbutton.href = "#";
delbutton.addEventListener("click", deleteEvent); delbutton.addEventListener("click", deleteEvent);
span.insertAdjacentElement("beforeend", a); span.insertAdjacentElement("beforeend", a);
span.innerHTML += '&nbsp;';
span.insertAdjacentElement("beforeend", delbutton); span.insertAdjacentElement("beforeend", delbutton);
document.querySelector("#tags").insertAdjacentElement("afterbegin", span); document.querySelector("#tags").insertAdjacentElement("afterbegin", span);
@@ -287,6 +288,7 @@ const flash = ({ type, msg }) => {
textfield.addEventListener("keyup", async e => { textfield.addEventListener("keyup", async e => {
if(e.key === 'Enter') { if(e.key === 'Enter') {
parent.removeChild(textfield);
// send // send
let res = await fetch('/api/v2/admin/tags/' + encodeURIComponent(oldtag), { let res = await fetch('/api/v2/admin/tags/' + encodeURIComponent(oldtag), {
method: 'PUT', method: 'PUT',
@@ -303,7 +305,7 @@ const flash = ({ type, msg }) => {
switch(status) { switch(status) {
case 200: // success, change case 200: // success, change
case 201: case 201:
parent.removeChild(textfield); //parent.removeChild(textfield);
parent.insertAdjacentElement('afterbegin', old); parent.insertAdjacentElement('afterbegin', old);
parent.querySelector('a:last-child').style.display = ''; parent.querySelector('a:last-child').style.display = '';
old.href = `/tag/${res.tag}`; old.href = `/tag/${res.tag}`;
+90 -15
View File
@@ -1,3 +1,11 @@
window.requestAnimFrame = (function(){
return window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| function(callback) { window.setTimeout(callback, 1000 / 60);};
})();
(() => { (() => {
let video; let video;
if(elem = document.querySelector("#my-video")) { if(elem = document.querySelector("#my-video")) {
@@ -8,6 +16,42 @@
document.querySelector('.v0ck_overlay').classList[video.paused ? 'remove' : 'add']('v0ck_hidden'); document.querySelector('.v0ck_overlay').classList[video.paused ? 'remove' : 'add']('v0ck_hidden');
} }
}); });
document.getElementById('togglebg').addEventListener('click', function (e) {
e.preventDefault();
background = !background;
localStorage.setItem('background', background.toString());
var canvas = document.getElementById('bg');
if (background) {
canvas.classList.add('fader-in');
canvas.classList.remove('fader-out');
} else {
canvas.classList.add('fader-out');
canvas.classList.remove('fader-in');
}
animationLoop();
});
if(elem !== null) {
if(localStorage.getItem('background') == undefined) {
localStorage.setItem('background', 'true');
}
var background = localStorage.getItem('background') === 'true';
var canvas = document.getElementById('bg');
var context = canvas.getContext('2d');
var cw = canvas.width = canvas.clientWidth | 0;
var ch = canvas.height = canvas.clientHeight | 0;
function animationLoop() {
if(video.paused || video.ended || !background)
return;
context.drawImage(video, 0, 0, cw, ch);
window.requestAnimFrame(animationLoop);
}
elem.addEventListener('play', animationLoop);
}
} }
let tt = false; let tt = false;
@@ -29,6 +73,8 @@
}; };
document.addEventListener("keydown", e => { document.addEventListener("keydown", e => {
if(e.key in keybindings && e.target.tagName !== "INPUT") { if(e.key in keybindings && e.target.tagName !== "INPUT") {
if(e.shiftKey || e.ctrlKey || e.metaKey || e.altKey)
return;
e.preventDefault(); e.preventDefault();
keybindings[e.key](); keybindings[e.key]();
} }
@@ -44,19 +90,44 @@
i.src = e.src; i.src = e.src;
}); });
// <wheeler>
const wheelEventListener = function(event) {
if (event.target.closest('.media-object')) {
if (event.deltaY < 0) {
document.getElementById('next').click();
} else if (event.deltaY > 0) {
document.getElementById('prev').click();
}
}
};
window.addEventListener('wheel', wheelEventListener);
// </wheeler>
if(f0ckimage = document.querySelector("img#f0ck-image")) { if(f0ckimage = document.querySelector("img#f0ck-image")) {
const f0ckimagescroll = document.querySelector("#image-scroll"); const f0ckimagescroll = document.querySelector("#image-scroll");
let isImageExpanded = false;
console.log("entry point - image unclicked")
f0ckimage.addEventListener("click", async e => { f0ckimage.addEventListener("click", async e => {
e.preventDefault(); e.preventDefault();
const img = await imgSize(f0ckimage); const img = await imgSize(f0ckimage);
if(img.width > img.height) console.log("img clicked");
return; if (isImageExpanded) {
f0ckimagescroll.hasAttribute("style") isImageExpanded = false;
? f0ckimagescroll.removeAttribute("style") f0ckimagescroll.removeAttribute("style");
: f0ckimagescroll.setAttribute("style", "overflow-y: scroll"); f0ckimage.removeAttribute("style");
f0ckimage.hasAttribute("style") console.log("image is not expanded")
? f0ckimage.removeAttribute("style") window.addEventListener('wheel', wheelEventListener);
: f0ckimage.setAttribute("style", "max-height: none; height: auto; width: 100%; position: absolute; left: 0;"); } else {
if (img.width > img.height) return;
isImageExpanded = true;
window.removeEventListener('wheel', wheelEventListener);
f0ckimagescroll.setAttribute("style", "overflow-y: scroll");
f0ckimage.setAttribute("style", "max-height: none; height: auto; width: 100%; position: absolute; left: 0; border: var(--img-border-width) solid var(--img-border-color); border-top: none; border-bottom: none;");
}
}); });
} }
// </image-responsive> // </image-responsive>
@@ -64,13 +135,13 @@
// <scroller> // <scroller>
let tts = 0; let tts = 0;
const scroll_treshold = 1; const scroll_treshold = 1;
if(document.querySelector("div#posts")) { if([...document.querySelectorAll("div.posts")].length === 1) {
document.addEventListener("wheel", e => { document.addEventListener("wheel", e => {
if(Math.ceil(window.innerHeight + window.scrollY) >= document.body.offsetHeight && e.deltaY > 0) { // down if(Math.ceil(window.innerHeight + window.scrollY) >= document.querySelector('#main').offsetHeight && e.deltaY > 0) { // down
if(elem = document.querySelector(".pagination > .next:not(.disabled)")) { if(elem = document.querySelector(".pagination > .next:not(.disabled)")) {
if(tts < scroll_treshold) { if(tts < scroll_treshold) {
document.querySelector("div#footbar").style.boxShadow = "inset 0px 4px 0px var(--accent)"; document.querySelector("div#footbar").style.boxShadow = "inset 0px 4px 0px var(--footbar-color)";
document.querySelector("div#footbar").style.color = "var(--accent)"; document.querySelector("div#footbar").style.color = "var(--footbar-color)";
tts++; tts++;
} }
else else
@@ -80,7 +151,7 @@
else if(window.scrollY <= 0 && e.deltaY < 0) { // up else if(window.scrollY <= 0 && e.deltaY < 0) { // up
if(elem = document.querySelector(".pagination > .prev:not(.disabled)")) { if(elem = document.querySelector(".pagination > .prev:not(.disabled)")) {
if(tts < scroll_treshold) { if(tts < scroll_treshold) {
document.querySelector("nav.navbar").style.boxShadow = "0px 2px 0px var(--accent)"; document.querySelector("nav.navbar").style.boxShadow = "0px 2px 0px var(--loading-indicator-color)";
document.querySelector("nav.navbar").style.transition = ".2s ease-in-out"; document.querySelector("nav.navbar").style.transition = ".2s ease-in-out";
tts++; tts++;
} }
@@ -150,10 +221,10 @@
} }
else { else {
if(Math.abs(swipeRT.yDiff) > swipeOpt.treshold && timeDiff < swipeOpt.timeout) { if(Math.abs(swipeRT.yDiff) > swipeOpt.treshold && timeDiff < swipeOpt.timeout) {
if(navbar = document.querySelector("nav.navbar") && document.querySelector("div#posts")) { if(navbar = document.querySelector("nav.navbar") && document.querySelector("div.posts")) {
if(swipeRT.yDiff > 0 && (window.innerHeight + window.scrollY) >= document.body.offsetHeight) // up if(swipeRT.yDiff > 0 && (window.innerHeight + window.scrollY) >= document.body.offsetHeight) // up
elem = document.querySelector(".pagination > .next:not(.disabled)"); elem = document.querySelector(".pagination > .next:not(.disabled)");
else if(swipeRT.yDiff <= 0 && window.scrollY <= 0 && document.querySelector("div#posts")) // down else if(swipeRT.yDiff <= 0 && window.scrollY <= 0 && document.querySelector("div.posts")) // down
elem = document.querySelector(".pagination > .prev:not(.disabled)"); elem = document.querySelector(".pagination > .prev:not(.disabled)");
} }
} }
@@ -232,4 +303,8 @@
}); });
} }
// </mediakeys> // </mediakeys>
// <scroller>
// </scroller>
})(); })();
+18
View File
@@ -8,6 +8,7 @@ const Cookie = {
opts['max-age'] = opts.days * 60 * 60 * 24; opts['max-age'] = opts.days * 60 * 60 * 24;
delete opts.days; delete opts.days;
} }
opts.SameSite = 'Strict';
opts = Object.entries(opts).reduce((accumulatedStr, [k, v]) => `${accumulatedStr}; ${k}=${v}`, ''); opts = Object.entries(opts).reduce((accumulatedStr, [k, v]) => `${accumulatedStr}; ${k}=${v}`, '');
document.cookie = name + '=' + encodeURIComponent(value) + opts document.cookie = name + '=' + encodeURIComponent(value) + opts
} }
@@ -44,4 +45,21 @@ const Cookie = {
Cookie.set("theme", themes[i], { path: "/", days: 360 }); Cookie.set("theme", themes[i], { path: "/", days: 360 });
} }
}); });
if(tbuttonfull = document.querySelector('svg#a_tfull')) {
tbuttonfull.addEventListener('click', e => {
let f = Cookie.get('fullscreen');
if(f == 1) {
Cookie.set('fullscreen', 0);
document.querySelector('html').setAttribute('res', '');
tbuttonfull.innerHTML = `<use href="/s/img/iconset.svg#window-maximize"></use>`;
}
else {
Cookie.set('fullscreen', 1);
document.querySelector('html').setAttribute('res', 'fullscreen');
tbuttonfull.innerHTML = `<use href="/s/img/iconset.svg#window-minimize"></use>`;
}
return true;
});
}
})(); })();
+1
View File
@@ -18,6 +18,7 @@ const tpl_player = svg => `<div class="v0ck_player_controls">
<input type="range" name="volume" min="0" max="1" step="0.01" value="1" /> <input type="range" name="volume" min="0" max="1" step="0.01" value="1" />
<button class="v0ck_player_button v0ck_playtime">00:00 / 00:00</button> <button class="v0ck_player_button v0ck_playtime">00:00 / 00:00</button>
<span style="flex: 30"></span> <span style="flex: 30"></span>
<button id="togglebg">&#128161;</button>
<button data-skip="-10" class="v0ck_player_button"> <button data-skip="-10" class="v0ck_player_button">
<svg style="width: 20px; height: 20px;"><use id="v0ck_svg_backward" href="${svg}#backward"></use></svg> <svg style="width: 20px; height: 20px;"><use id="v0ck_svg_backward" href="${svg}#backward"></use></svg>
</button> </button>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+32
View File
@@ -0,0 +1,32 @@
import fetch from 'flumm-fetch';
import cfg from './config.mjs';
export default new class autotagger {
async isNSFW(filename, filesize) {
let opts = {
method: 'POST',
};
let apiurl;
if(filesize < 4194304) {
apiurl = cfg.apis.nsfw1.url;
opts.headers = cfg.apis.nsfw1.headers;
opts.body = JSON.stringify({
DataRepresentation: "URL",
Value: `${cfg.main.url.full}/b/${filename}`
});
}
else {
apiurl = cfg.apis.nsfw2.url;
opts.headers = cfg.apis.nsfw2.headers;
opts.body = JSON.stringify({
url: `${cfg.main.url.full}/b/${filename}`
})
}
const res = await (await fetch(apiurl, opts)).json();
if(filesize < 4194304)
return res.IsImageAdultClassified || res.RacyClassificationScore > 0.6;
else
return res.unsafe;
};
};
+318
View File
@@ -0,0 +1,318 @@
import logger from "../log.mjs";
import { getLevel } from "../../inc/admin.mjs";
import { promises as fs } from "fs";
import cfg from "../config.mjs";
import db from "../sql.mjs";
import lib from "../lib.mjs";
const tagkeyboard = id => {
const tags = [{
tag: 'music',
id: 115
}, {
tag: 'german',
id: 329
}, {
tag: 'cat',
id: 217
}, {
tag: 'doggo',
id: 5
}];
return Promise.all(tags.map(async t => ({ text: `${await lib.hasTag(id, t.id) ? '✓ ' : ''}${t.tag}`, callback_data: `b_settag_${t.id}:${id}` })));
};
export default async bot => {
return [{
name: "callback_query",
listener: "callback_query",
f: async e => {
logger.info(`${e.network} -> ${e.channel} -> ${e.user.nick}: ${e.message}`);
let [ cmd, id ] = e.opt.data.split(':');
let f0ck;
id = +id;
if(cmd.startsWith('b_settag_')) {
const tagid = +cmd.replace('b_settag_', '');
if(!(await lib.getTags(id)).filter(tag => tag.id == tagid).length) {
// insert
await db`
insert into "tags_assign" ${
db({
item_id: id,
tag_id: tagid,
user_id: 1
})
}
`;
}
else {
// delete
await db`
delete from "tags_assign"
where tag_id = ${tagid}
and item_id = ${id}
`;
}
const keyboard = await tagkeyboard(id);
return await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
reply_markup: JSON.stringify({
inline_keyboard: [[
...keyboard
], [
{ text: 'back', callback_data: `b_back:${id}` }
]]
})
});
}
switch(cmd) {
case "b_tags":
if(!id)
return;
const keyboard = await tagkeyboard(id);
await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
reply_markup: JSON.stringify({
inline_keyboard: [[
...keyboard
], [
{ text: 'back', callback_data: `b_back:${id}` }
]]
})
});
break;
case "b_back":
if(!id)
return;
await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
reply_markup: JSON.stringify({
inline_keyboard: [[
{ text: (await lib.hasTag(id, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${id}` },
{ text: (await lib.hasTag(id, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${id}` },
{ text: 'tags', callback_data: `b_tags:${id}` },
{ text: '❌ delete', callback_data: `b_delete:${id}` }
], [
{ text: `open f0ck #${id}`, url: `${cfg.main.url.full}/${id}` }
]]
})
});
break;
case "b_sfw":
if(!id)
return;
if(!await lib.hasTag(id, 1)) {
// insert
await db`
insert into "tags_assign" ${
db({
item_id: id,
tag_id: 1, // sfw
user_id: 1
})
}
`;
if(await lib.hasTag(id, 2)) {
await db`
delete from "tags_assign"
where tag_id = 2
and item_id = ${id}
`;
}
}
else {
// delete
await db`
delete from "tags_assign"
where tag_id = 1
and item_id = ${id}
`;
}
return await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
reply_markup: JSON.stringify({
inline_keyboard: [[
{ text: (await lib.hasTag(id, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${id}` },
{ text: (await lib.hasTag(id, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${id}` },
{ text: 'tags', callback_data: `b_tags:${id}` },
{ text: '❌ delete', callback_data: `b_delete:${id}` }
], [
{ text: `open f0ck #${id}`, url: `${cfg.main.url.full}/${id}` }
]]
})
});
break;
case "b_nsfw":
if(!id)
return;
if(!await lib.hasTag(id, 2)) {
// insert
await db`
insert into "tags_assign" ${
db({
item_id: id,
tag_id: 2, // nsfw
user_id: 1
})
}
`;
if(await lib.hasTag(id, 1)) {
await db`
delete from "tags_assign"
where tag_id = 1
and item_id = ${id}
`;
}
}
else {
// delete
await db`
delete from "tags_assign"
where tag_id = 2
and item_id = ${id}
`;
}
return await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
reply_markup: JSON.stringify({
inline_keyboard: [[
{ text: (await lib.hasTag(id, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${id}` },
{ text: (await lib.hasTag(id, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${id}` },
{ text: 'tags', callback_data: `b_tags:${id}` },
{ text: '❌ delete', callback_data: `b_delete:${id}` }
], [
{ text: `open f0ck #${id}`, url: `${cfg.main.url.full}/${id}` }
]]
})
});
break;
case "b_delete":
if(id <= 1)
return;
e.user = {
prefix: `${e.raw.reply_to_message.from.username}!${e.raw.reply_to_message.from.id}@${e.network}`,
nick: e.raw.reply_to_message.from.first_name,
username: e.raw.reply_to_message.from.username,
account: e.raw.reply_to_message.from.id.toString()
};
f0ck = await db`
select dest, mime, username, userchannel, usernetwork
from "items"
where
id = ${id} and
active = 'true'
limit 1
`;
const level = getLevel(e.user).level;
if(f0ck.length === 0) {
return await e.reply(`f0ck ${id}: f0ck not found`);
}
if(
(f0ck[0].username !== (e.user.nick || e.user.username) ||
f0ck[0].userchannel !== e.channel ||
f0ck[0].usernetwork !== e.network) &&
level < 100
) {
return await e.reply(`f0ck ${id}: insufficient permissions`);
}
if(~~(new Date() / 1e3) >= (f0ck[0].stamp + 600) && level < 100) {
return await e.reply(`f0ck ${id}: too late lol`);
}
await db`update "items" set active = 'false' where id = ${id}`;
await fs.copyFile(`./public/b/${f0ck[0].dest}`, `./deleted/b/${f0ck[0].dest}`).catch(_=>{});
await fs.copyFile(`./public/t/${id}.webp`, `./deleted/t/${id}.webp`).catch(_=>{});
await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(_=>{});
await fs.unlink(`./public/t/${id}.webp`).catch(_=>{});
if(f0ck[0].mime.startsWith('audio')) {
await fs.copyFile(`./public/ca/${id}.webp`, `./deleted/ca/${id}.webp`).catch(_=>{});
await fs.unlink(`./public/ca/${id}.webp`).catch(_=>{});
}
await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
reply_markup: JSON.stringify({
inline_keyboard: [[
{ text: (await lib.hasTag(id, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${id}` },
{ text: (await lib.hasTag(id, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${id}` },
{ text: 'tags', callback_data: `b_tags:${id}` },
{ text: 'recover', callback_data: `b_recover:${id}` }
], [
{ text: `open f0ck #${id}`, url: `${cfg.main.url.full}/${id}` }
]]
})
});
break;
case "b_recover":
if(id <= 1)
return;
e.user = {
prefix: `${e.raw.reply_to_message.from.username}!${e.raw.reply_to_message.from.id}@${e.network}`,
nick: e.raw.reply_to_message.from.first_name,
username: e.raw.reply_to_message.from.username,
account: e.raw.reply_to_message.from.id.toString()
};
f0ck = await db`
select dest, mime
from "items"
where
id = ${id} and
active = 'false'
limit 1
`;
if(f0ck.length === 0) {
return await e.reply(`f0ck ${id}: f0ck not found`);
}
await fs.copyFile(`./deleted/b/${f0ck[0].dest}`, `./public/b/${f0ck[0].dest}`).catch(_=>{});
await fs.copyFile(`./deleted/t/${id}.webp`, `./public/t/${id}.webp`).catch(_=>{});
await fs.unlink(`./deleted/b/${f0ck[0].dest}`).catch(_=>{});
await fs.unlink(`./deleted/t/${id}.webp`).catch(_=>{});
if(f0ck[0].mime.startsWith('audio')) {
await fs.copyFile(`./deleted/ca/${id}.webp`, `./public/ca/${id}.webp`).catch(_=>{});
await fs.unlink(`./deleted/ca/${id}.webp`).catch(_=>{});
}
await db`update "items" set active = 'true' where id = ${id}`;
await e.editMessageText(e.raw.chat.id, e.raw.message_id, e.message, {
reply_markup: JSON.stringify({
inline_keyboard: [[
{ text: (await lib.hasTag(id, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${id}` },
{ text: (await lib.hasTag(id, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${id}` },
{ text: 'tags', callback_data: `b_tags:${id}` },
{ text: '❌ delete', callback_data: `b_delete:${id}` }
], [
{ text: `open f0ck #${id}`, url: `${cfg.main.url.full}/${id}` }
]]
})
});
break;
default:
await e.reply('lol');
}
}
}];
};
+2 -2
View File
@@ -20,7 +20,7 @@ export default async bot => {
let trigger; let trigger;
if(e.photo) { if(e.media) {
trigger = [...bot._trigger.entries()].filter(t => t[1].name === "parser"); trigger = [...bot._trigger.entries()].filter(t => t[1].name === "parser");
if(!e.message) if(!e.message)
e.message = ""; e.message = "";
@@ -40,7 +40,7 @@ export default async bot => {
} }
catch(err) { catch(err) {
console.error(err); console.error(err);
e.reply(`${t[0]}: An error occured.`); await e.reply(`${t[0]}: An error occured.`);
logger.error(`${e.network} -> ${e.channel} -> ${e.user.nick}: ${err.toString ? err : JSON.stringify(err)}`); logger.error(`${e.network} -> ${e.channel} -> ${e.user.nick}: ${err.toString ? err : JSON.stringify(err)}`);
} }
}); });
+44 -13
View File
@@ -63,7 +63,7 @@ export default new class {
const link = []; const link = [];
if(env.tag) link.push("tag", env.tag); if(env.tag) link.push("tag", env.tag);
if(env.user) link.push("user", env.user, env.type ?? 'f0cks'); if(env.user) link.push("user", env.user, env.type ?? 'f0cks');
if(env.mime.length > 2) link.push(env.mime); if(env.mime?.length > 2) link.push(env.mime);
let tmp = link.length === 0 ? '/' : link.join('/'); let tmp = link.length === 0 ? '/' : link.join('/');
if(!tmp.endsWith('/')) if(!tmp.endsWith('/'))
@@ -84,32 +84,43 @@ export default new class {
// async funcs // async funcs
async countf0cks() { async countf0cks() {
const tagged = (await db` const tagged = +(await db`
select count(*) as total select count(*) as total
from "items" from "items"
where id in (select item_id from tags_assign group by item_id) where id in (select item_id from tags_assign group by item_id) and active = true
`)[0].total; `)[0].total;
const untagged = (await db` const untagged = +(await db`
select count(*) as total select count(*) as total
from "items" from "items"
where id not in (select item_id from tags_assign group by item_id) where id not in (select item_id from tags_assign group by item_id) and active = true
`)[0].total; `)[0].total;
const sfw = (await db` const sfw = +(await db`
select count(*) as total select count(*) as total
from "items" from "items"
where id in (select item_id from tags_assign where tag_id = 1 group by item_id) where id in (select item_id from tags_assign where tag_id = 1 group by item_id) and active = true
`)[0].total; `)[0].total;
const nsfw = (await db` const nsfw = +(await db`
select count(*) as total select count(*) as total
from "items" from "items"
where id in (select item_id from tags_assign where tag_id = 2 group by item_id) where id in (select item_id from tags_assign where tag_id = 2 group by item_id) and active = true
`)[0].total; `)[0].total;
const deleted = +(await db`
select count(*) as total
from "items"
where active = false
`)[0].total;
const lastf0ck = +(await db`
select max(id) as id
from "items"
`)[0].id;
return { return {
tagged, tagged,
untagged, untagged,
total: +tagged + +untagged, total: tagged + untagged,
deleted,
untracked: lastf0ck - (tagged + untagged + deleted),
sfw, sfw,
nsfw nsfw,
}; };
}; };
async hash(str) { async hash(str) {
@@ -161,15 +172,25 @@ export default new class {
} }
return tags; return tags;
}; };
async hasTag(itemid, tagid) {
const tag = (await db`
select *
from "tags_assign"
where
item_id = ${+itemid} and
tag_id = ${+tagid}
limit 1
`).length;
return !!tag;
};
async detectNSFW(dest) { async detectNSFW(dest) {
return false;
const { stdout, stderr } = await exec( const { stdout, stderr } = await exec(
`python -c "import sys\nfrom nsfw_detector import predict\nmodel = predict.load_model('./nsfw_model.h5')\nprint(predict.classify(model, './public/b/${dest}'))"` `python -c "import sys\nfrom nsfw_detector import predict\nmodel = predict.load_model('./nsfw_model.h5')\nprint(predict.classify(model, './public/b/${dest}'))"`
); );
const res = JSON.parse(stdout.replace(/\'/g, '"').split('\n').slice(1, -1)); const res = JSON.parse(stdout.replace(/\'/g, '"').split('\n').slice(1, -1));
const tmp = Object.values(res)[0]; const tmp = Object.values(res)[0];
tmp.sexy = tmp.sexy / 2;
let nsfw = false; let nsfw = false;
if(tmp.neutral >= .7) if(tmp.neutral >= .7)
nsfw = false; nsfw = false;
@@ -187,5 +208,15 @@ export default new class {
}; };
}; };
async getDefaultAvatar() {
return (await db`
select column_default as avatar
from "information_schema"."columns"
where
TABLE_SCHEMA='public' and
TABLE_NAME='user_options' and
COLUMN_NAME = 'avatar'
`)[0].avatar;
}
}; };
+122
View File
@@ -0,0 +1,122 @@
import fetch from "flumm-fetch";
import { exec as _exec } from "child_process";
import fs from "fs";
import db from "./sql.mjs";
export default new class queue {
constructor() {
};
addqueue(e, link) {
//this.#queue.push(e, link);
};
exec(cmd) {
return new Promise((resolve, reject) => {
_exec(cmd, { maxBuffer: 5e3 * 1024 }, (err, stdout, stderr) => {
if(err)
return reject(err);
if(stderr)
console.error(stderr);
resolve({ stdout: stdout });
});
});
};
async genuuid() {
return (await db`
select gen_random_uuid() as uuid
`)[0].uuid.substring(0, 8);
};
async checkrepostlink(link) {
const q_repost = await db`
select id
from "items"
where src = ${link}
`;
return q_repost.length > 0 ? q_repost[0].id : false;
};
async checkrepostsum(checksum) {
const q_repost = await db`
select id
from "items"
where checksum = ${checksum}
`;
return q_repost.length > 0 ? q_repost[0].id : false;
};
async getItemID(filename) {
return (await db`
select *
from "items"
where dest = ${filename}
limit 1
`)[0].id;
};
async genThumbnail(filename, mime, itemid, link) {
if(mime.startsWith('video/') || mime == 'image/gif')
await this.exec(`ffmpegthumbnailer -i./public/b/${filename} -s1024 -o./tmp/${itemid}.png`);
else if(mime.startsWith('image/') && mime != 'image/gif')
await this.exec(`convert "./public/b/${filename}[0]" ./tmp/${itemid}.png`);
else if(mime.startsWith('audio/')) {
if(link.match(/soundcloud/)) {
let cover = (await this.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --get-thumbnail "${link}"`)).stdout.trim();
if(!cover.match(/default_avatar/)) {
cover = cover.replace(/-(large|original)\./, '-t500x500.');
try {
await this.exec(`wget "${cover}" -O ./tmp/${itemid}.jpg`);
const size = (await fs.promises.stat(`./tmp/${itemid}.jpg`)).size;
if(size >= 0) {
await this.exec(`convert ./tmp/${itemid}.jpg ./tmp/${itemid}.png`);
await this.exec(`convert ./tmp/${itemid}.jpg ./public/ca/${itemid}.webp`);
}
} catch(err) {}
}
else {
await this.exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`);
await this.exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
}
}
else {
await this.exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`);
await this.exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
}
}
await this.exec(`convert "./tmp/${itemid}.png" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.webp`);
await fs.promises.unlink(`./tmp/${itemid}.png`).catch(_=>{});
await fs.promises.unlink(`./tmp/${itemid}.jpg`).catch(_=>{});
return true;
};
// tags
async tagSFW(itemid) {
return await db`
insert into "tags_assign" ${
db({
item_id: itemid,
tag_id: 1,
user_id: 1
})
}
`;
};
async tagNSFW(itemid) {
return await db`
insert into "tags_assign" ${
db({
item_id: itemid,
tag_id: 2,
user_id: 1
})
}
`;
};
};
+86 -256
View File
@@ -4,158 +4,74 @@ import cfg from "../config.mjs";
import fs from "fs"; import fs from "fs";
import url from "url"; import url from "url";
const globalfilter = cfg.nsfp.map(n => `tag_id = ${n}`).join(' or ');
export default { export default {
getf0cks: async (o = { user, tag, mime, page, mode, fav }) => { getf0cks: async (o = { user, tag, mime, page, mode, fav, session }) => {
const user = o.user ? decodeURI(o.user) : null; const user = o.user ? decodeURI(o.user) : null;
const tag = lib.parseTag(o.tag ?? null); const tag = lib.parseTag(o.tag ?? null);
const mime = (o.mime ?? ""); const mime = o.mime ?? null;
const page = +(o.page ?? 1); const page = +(o.page ?? 1);
const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%"; const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%";
const tmp = { user, tag, mime, smime, page }; const tmp = { user, tag, mime, smime, page, mode: o.mode };
const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0); const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0);
let data; const total = (await db`
let total; select distinct on (items.id)
count(items.id) as total
if(tag) {
if(tag.match(/sfw/) || tag.length <= 2)
return {
success: false,
message: "nope."
};
total = await db`
select count(*) as total
from items from items
inner join ( left join tags_assign on tags_assign.item_id = items.id
select tags_assign.item_id, tags.tag left join tags on tags.id = tags_assign.tag_id
from tags left join favorites on favorites.item_id = items.id
left join tags_assign on tags_assign.tag_id = tags.id left join "user" on "user".id = favorites.user_id
where tags.tag ilike ${'%' + (tag ? tag : '') + '%'} where
group by tags_assign.item_id, tags.tag ${ db.unsafe(modequery) }
) as st on st.item_id = items.id and items.active = 'true'
where ${db.unsafe(modequery)} ${ tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db`` }
group by st.tag, st.item_id`; ${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` }
${ !o.fav && user ? db`and items.username ilike ${'%'+user+'%'}` : db`` }
${ mime ? db`and items.mime ilike ${smime}` : db`` }
${ !o.session && globalfilter ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
group by items.id, tags.tag
`)?.length || 0;
total = total?.length; if(!total || total === 0) {
}
else {
if(!o.fav) {
total = await db`
select count(*) as total
from items
where ${db.unsafe(modequery)}
and items.mime ilike ${smime}
and items.username ilike ${user ? user : '%'}
`;
total = total[0].total;
}
else {
total = await db`
select count(*) as total
from "favorites"
left join "user" on "user".id = "favorites".user_id
left join "tags_assign" on "tags_assign".item_id = "favorites".item_id
left join "tags" on "tags".id = "tags_assign".tag_id
left join "items" on "items".id = "favorites".item_id
where ${db.unsafe(modequery)}
and "items".mime ilike ${smime}
and "user".user ilike ${user}
group by "items".id
`;
total = total[0].total;
}
}
if(!total || total.length === 0)
return { return {
success: false, success: false,
message: "404 - no f0cks given" message: "404 - no f0cks given"
}; };
}
const pages = +Math.ceil(total / cfg.websrv.eps); const pages = +Math.ceil(total / cfg.websrv.eps);
const act_page = Math.min(pages, page || 1); const act_page = Math.min(pages, page || 1);
const offset = Math.max(0, (act_page - 1) * cfg.websrv.eps); const offset = Math.max(0, (act_page - 1) * cfg.websrv.eps);
let rows; const rows = await db`
select distinct on (items.id)
if(!o.fav) { items.id,
rows = db` items.mime,
select "items".id, "items".mime, "tags_assign".tag_id tags.tag,
from "items" ta.tag_id
left join "tags_assign" on "tags_assign".item_id = "items".id and ("tags_assign".tag_id = 1 or "tags_assign".tag_id = 2) from items
${tag left join tags_assign on tags_assign.item_id = items.id
? db` left join tags on tags.id = tags_assign.tag_id
inner join ( left join favorites on favorites.item_id = items.id
select "tags_assign".item_id, "tags".tag left join "user" on "user".id = favorites.user_id
from "tags" left join tags_assign ta on ta.item_id = items.id and (ta.tag_id = 1 or ta.tag_id = 2)
left join "tags_assign" on "tags_assign".tag_id = "tags".id where
where "tags".tag ilike ${'%' + tag + '%'} ${ db.unsafe(modequery) }
group by "tags_assign".item_id, "tags".tag and items.active = 'true'
) as st on st.item_id = "items".id ${ tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db`` }
` ${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` }
: db`` ${ !o.fav && user ? db`and items.username ilike ${'%'+user+'%'}` : db`` }
} ${ mime ? db`and items.mime ilike ${smime}` : db`` }
where ${db.unsafe(modequery)} ${ !o.session && globalfilter ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
and "items".mime ilike ${smime} group by items.id, tags.tag, ta.tag_id
and "items".username ilike ${user ? user : '%'} order by items.id desc
${tag
? db`group by st.item_id, "items".id, "tags_assign".tag_id`
: db``
}
order by "items".id desc
offset ${offset} offset ${offset}
limit ${cfg.websrv.eps} limit ${cfg.websrv.eps}
`; `;
}
else {
rows = db`
select "items".id, "items".mime, ta.tag_id
from "favorites"
left join "user" on "user".id = "favorites".user_id
left join "tags_assign" on "tags_assign".item_id = "favorites".item_id
left join "tags" on "tags".id = "tags_assign".tag_id
left join "items" on "items".id = "favorites".item_id
left join "tags_assign" as ta on ta.item_id = "items".id and (ta.tag_id = 1 or ta.tag_id = 2)
${ tag
? db`
inner join (
select "tags_assign".item_id, "tags".tag
from "tags"
left join "tags_assign" on "tags_assign".tag_id = "tags".id
where "tags".tag ilike ${'%' + tag + '%'}
group by "tags_assign".item_id, "tags".tag
) as st on st.item_id = "items".id`
: db``
}
where ${db.unsafe(modequery)}
and "items".mime ilike ${smime}
and "user".user ilike ${user}
${tag
? db`group by st.item_id, "items".id, "tags_assign".tag_id`
: db``
}
group by "items".id, ta.tag_id
order by "items".id desc
offset ${offset}
limit ${cfg.websrv.eps}
`;
}
rows = await rows;
if(rows.length === 0)
return {
success: false,
message: "oopsi woopsi"
};
/*rows.forEach(e => {
if(!fs.existsSync(`public/t/${e.id}.png`))
fs.copyFileSync("public/s/img/broken.png", `public/t/${e.id}.png`);
});*/
const cheat = []; const cheat = [];
for(let i = Math.max(1, act_page - 3); i <= Math.min(act_page + 3, pages); i++) for(let i = Math.max(1, act_page - 3); i <= Math.min(act_page + 3, pages); i++)
@@ -163,7 +79,7 @@ export default {
const link = lib.genLink({ user, tag, mime, type: o.fav ? 'favs' : 'f0cks', path: 'p/' }); const link = lib.genLink({ user, tag, mime, type: o.fav ? 'favs' : 'f0cks', path: 'p/' });
data = { return {
success: true, success: true,
items: rows, items: rows,
pagination: { pagination: {
@@ -177,9 +93,8 @@ export default {
link, link,
tmp tmp
}; };
return data;
}, },
getf0ck: async (o = ({ user, tag, mime, itemid, mode })) => { getf0ck: async (o = ({ user, tag, mime, itemid, mode, session })) => {
const user = o.user ? decodeURI(o.user) : null; const user = o.user ? decodeURI(o.user) : null;
const tag = lib.parseTag(o.tag ?? null); const tag = lib.parseTag(o.tag ?? null);
const mime = (o.mime ?? ""); const mime = (o.mime ?? "");
@@ -197,74 +112,26 @@ export default {
}; };
} }
let items; const items = await db`
select distinct on (items.id)
if(o.fav) { items.*
items = db` from items
select "items".* left join tags_assign on tags_assign.item_id = items.id
from "favorites" left join tags on tags.id = tags_assign.tag_id
left join "items" on "items".id = "favorites".item_id left join favorites on favorites.item_id = items.id
left join "user" on "user".id = "favorites".user_id left join "user" on "user".id = favorites.user_id
${ tag left join tags_assign ta on ta.item_id = items.id and (ta.tag_id = 1 or ta.tag_id = 2)
? db` where
inner join ( ${ db.unsafe(modequery) }
select "tags_assign".item_id, "tags".tag and items.active = 'true'
from "tags" ${ tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db`` }
left join "tags_assign" on "tags_assign".tag_id = "tags".id ${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` }
where "tags".tag ilike ${'%' + tag + '%'} ${ !o.fav && user ? db`and items.username ilike ${'%'+user+'%'}` : db`` }
group by "tags_assign".item_id, "tags".tag ${ mime ? db`and items.mime ilike ${smime}` : db`` }
) as st on st.item_id = "items".id` ${ !o.session && globalfilter ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
: db`` group by items.id, tags.tag, ta.tag_id
} order by items.id desc
where ${db.unsafe(modequery)}
and "user".user ilike ${user}
${ mime
? db`and "items".mime ilike ${mime + '/%'}`
: db``
}
${ tag
? db`group by st.tag, st.item_id, "items".id`
: db`group by "items".id, "favorites".user_id, "favorites".item_id, "user".id`
}
order by "items".id desc
`; `;
}
else {
items = db`
select "items".*
from "items"
${ tag
? db`
inner join (
select "tags_assign".item_id, "tags_assign".tag_id, "tags".tag
from "tags"
left join "tags_assign" on "tags_assign".tag_id = "tags".id
where "tags".tag ilike ${'%' + tag + '%'}
group by "tags_assign".item_id, "tags".tag, "tags_assign".tag_id
) as st on st.item_id = "items".id`
: db``
}
where ${db.unsafe(modequery)}
${ user
? db`and "items".username ilike ${'%' + user + '%'}`
: db``
}
${ mime
? db`and "items".mime ilike ${mime + '/%'}`
: db``
}
${ tag
? db`group by st.item_id, "items".id, st.tag_id`
: db`group by "items".id`
}
order by "items".id desc
`;
}
items = await items;
if(tag)
items = items.filter((v, i, s) => i === s.findIndex(t => t.id === v.id));
const item = items.findIndex(i => i.id === itemid); const item = items.findIndex(i => i.id === itemid);
const actitem = items[item]; const actitem = items[item];
@@ -334,71 +201,34 @@ export default {
}; };
return data; return data;
}, },
getRandom: async (o = ({ user, tag, mime, mode })) => { getRandom: async (o = ({ user, tag, mime, mode, session })) => {
const user = o.user ? decodeURI(o.user) : null; const user = o.user ? decodeURI(o.user) : null;
const tag = lib.parseTag(o.tag ?? null); const tag = lib.parseTag(o.tag ?? null);
const mime = (o.mime ?? ""); const mime = (o.mime ?? "");
const smime = cfg.allowedMimes.includes(mime) ? mime + "/%" : mime === "" ? "%" : "%";
const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0); const modequery = mime == "audio" ? lib.getMode(0) : lib.getMode(o.mode ?? 0);
let item; const item = await db`
if(o.fav) { // dood lol select
item = db` items.id
select "items".* from items
from "favorites" left join tags_assign on tags_assign.item_id = items.id
left join "items" on "items".id = "favorites".item_id left join tags on tags.id = tags_assign.tag_id
left join "user" on "user".id = "favorites".user_id left join favorites on favorites.item_id = items.id
${ tag left join "user" on "user".id = favorites.user_id
? db` where
inner join ( ${ db.unsafe(modequery) }
select "tags_assign".item_id, "tags".tag and items.active = 'true'
from "tags" ${ tag ? db`and tags.normalized ilike '%' || slugify(${tag}) || '%'` : db`` }
left join "tags_assign" on "tags_assign".tag_id = "tags".id ${ o.fav ? db`and "user".user ilike ${'%'+user+'%'}` : db`` }
where "tags".tag ilike ${'%' + tag + '%'} ${ user ? db`and items.username ilike ${'%'+user+'%'}` : db`` }
group by "tags_assign".item_id, "tags".tag ${ mime ? db`and items.mime ilike ${smime}` : db`` }
) as st on st.item_id = "items".id` ${ !o.session && globalfilter ? db`and items.id not in (select item_id from tags_assign where item_id = items.id and (${db.unsafe(globalfilter)}))` : db`` }
: db`` group by items.id, tags.tag
}
where ${db.unsafe(modequery)}
and "user".user ilike ${user}
${ mime
? db`and "items".mime ilike ${mime + '/%'}`
: db``
}
order by random() order by random()
limit 1 limit 1
`; `;
}
else {
item = db`
select *
from "items"
${ tag
? db`
inner join (
select "tags_assign".item_id, "tags".tag
from "tags"
left join "tags_assign" on "tags_assign".tag_id = "tags".id
where "tags".tag ilike ${'%' + tag + '%'}
group by "tags_assign".item_id, "tags".tag
) as st on st.item_id = "items".id`
: db``
}
where ${db.unsafe(modequery)}
${ user
? db`and "items".username ilike ${'%' + user + '%'}`
: db``
}
${ mime
? db`and "items".mime ilike ${mime + '/%'}`
: db``
}
order by random()
limit 1
`;
}
item = await item;
if(item.length === 0) { if(item.length === 0) {
return { return {
+79 -5
View File
@@ -1,6 +1,7 @@
import db from "../sql.mjs"; import db from "../sql.mjs";
import lib from "../lib.mjs"; import lib from "../lib.mjs";
import { exec } from "child_process"; import { exec } from "child_process";
import { promises as fs } from "fs";
const auth = async (req, res, next) => { const auth = async (req, res, next) => {
if(!req.session) { if(!req.session) {
@@ -15,8 +16,14 @@ const auth = async (req, res, next) => {
export default (router, tpl) => { export default (router, tpl) => {
router.get(/^\/login(\/)?$/, async (req, res) => { router.get(/^\/login(\/)?$/, async (req, res) => {
if(req.cookies.session) if(req.cookies.session) {
return res.reply({ body: "du bist schon eingeloggt lol" }); return res.reply({
body: tpl.render('error', {
message: "you're already logged in lol",
tmp: null
}, req)
});
}
res.reply({ res.reply({
body: tpl.render("login", { theme: req.cookies.theme ?? "f0ck" }) body: tpl.render("login", { theme: req.cookies.theme ?? "f0ck" })
}); });
@@ -99,7 +106,11 @@ export default (router, tpl) => {
router.get(/^\/admin(\/)?$/, auth, async (req, res) => { // frontpage router.get(/^\/admin(\/)?$/, auth, async (req, res) => { // frontpage
res.reply({ res.reply({
body: tpl.render("admin", { totals: await lib.countf0cks(), session: req.session }, req) body: tpl.render("admin", {
totals: await lib.countf0cks(),
session: req.session,
tmp: null
}, req)
}); });
}); });
@@ -115,7 +126,8 @@ export default (router, tpl) => {
body: tpl.render("admin/sessions", { body: tpl.render("admin/sessions", {
session: req.session, session: req.session,
sessions: rows, sessions: rows,
totals: await lib.countf0cks() totals: await lib.countf0cks(),
tmp: null
}, req) }, req)
}); });
}); });
@@ -124,11 +136,73 @@ export default (router, tpl) => {
exec("journalctl -qeu f0ck --no-pager", (err, stdout) => { exec("journalctl -qeu f0ck --no-pager", (err, stdout) => {
res.reply({ res.reply({
body: tpl.render("admin/log", { body: tpl.render("admin/log", {
log: stdout.split("\n").slice(0, -1) log: stdout.split("\n").slice(0, -1),
tmp: null
}, req) }, req)
}); });
}); });
}); });
router.get(/^\/admin\/recover\/?/, auth, async (req, res) => {
if(req.url.qs?.id) {
const id = +req.url.qs.id;
const f0ck = await db`
select dest, mime
from "items"
where
id = ${id} and
active = 'false'
limit 1
`;
if(f0ck.length === 0) {
return res.reply({
body: `f0ck ${id}: f0ck not found`
});
}
await db`update "items" set active = 'true' where id = ${id}`;
await fs.copyFile(`./deleted/b/${f0ck[0].dest}`, `./public/b/${f0ck[0].dest}`).catch(_=>{});
await fs.copyFile(`./deleted/t/${id}.webp`, `./public/t/${id}.webp`).catch(_=>{});
await fs.unlink(`./deleted/b/${f0ck[0].dest}`).catch(_=>{});
await fs.unlink(`./deleted/t/${id}.webp`).catch(_=>{});
if(f0ck[0].mime.startsWith('audio')) {
await fs.copyFile(`./deleted/ca/${id}.webp`, `./public/ca/${id}.webp`).catch(_=>{});
await fs.unlink(`./deleted/ca/${id}.webp`).catch(_=>{});
}
return res.reply({
body: `f0ck ${id} recovered. <a href="/admin/recover">back</a>`
});
}
const _posts = await db`
select id, mime, username
from "items"
where
active = 'false'
order by id desc
`;
if(_posts.length === 0) {
return res.reply({
body: 'blah'
});
}
const posts = await Promise.all(_posts.map(async p => ({
...p,
thumbnail: (await fs.readFile(`./deleted/t/${p.id}.webp`)).toString('base64')
})));
res.reply({
body: tpl.render('admin/recover', {
posts,
tmp: null
}, req)
});
});
return router; return router;
}; };
+78 -26
View File
@@ -1,3 +1,4 @@
import { promises as fs } from "fs";
import db from '../../sql.mjs'; import db from '../../sql.mjs';
import lib from '../../lib.mjs'; import lib from '../../lib.mjs';
import search from '../../routeinc/search.mjs'; import search from '../../routeinc/search.mjs';
@@ -16,8 +17,10 @@ export default router => {
const rows = await db` const rows = await db`
select * select *
from "items" from "items"
where mime ilike ${mime} where
and username ilike ${user} mime ilike ${mime} and
username ilike ${user} and
active = 'true'
order by random() order by random()
limit 1 limit 1
`; `;
@@ -28,27 +31,47 @@ export default router => {
}); });
}); });
group.get(/\/p\/([0-9]+)/, async (req, res) => { // legacy group.get(/\/items\/get/, async (req, res) => {
let eps = 100; let eps = 150;
let id = +req.url.split[3];
const rows = await db` const opt = {
select * older: req.url.qs.older ?? null,
from "items" newer: req.url.qs.newer ?? null,
where id < ${+id} mode: +req.url.qs.mode ?? 0 // 0 sfw, 1 nsfw, 2 untagged, 3 all
order by id desc
limit ${+eps}
`;
const items = {
items: rows,
last: rows[rows.length - 1].id
}; };
const newest = (await db`select max(id) as id from "items"`)[0].id;
const oldest = (await db`select min(id) as id from "items"`)[0].id;
const modequery = lib.getMode(opt.mode);
const rows = (await db`
select "items".id, "items".mime, coalesce("tags_assign".tag_id, 0) as tag_id
from "items"
left join "tags_assign" on "tags_assign".item_id = "items".id and ("tags_assign".tag_id = 1 or "tags_assign".tag_id = 2)
where
${db.unsafe(modequery)} and
active = 'true'
${
opt.older
? db`and id <= ${opt.older}`
: opt.newer
? db`and id >= ${opt.newer}`
: db``
}
order by id ${
opt.newer
? db`asc`
: db`desc`
}
limit ${eps}
`).sort((a, b) => b.id - a.id);
return res.json({ return res.json({
atEnd: rows[0].id === newest,
atStart: rows[rows.length - 1].id === oldest,
success: true, success: true,
items items: rows
}); }, 200);
}); });
group.get(/\/item\/[0-9]+$/, async (req, res) => { group.get(/\/item\/[0-9]+$/, async (req, res) => {
@@ -57,20 +80,20 @@ export default router => {
const item = await db` const item = await db`
select * select *
from "items" from "items"
where id = ${+id} where id = ${+id} and active = 'true'
limit 1 limit 1
`; `;
const next = await db` const next = await db`
select id select id
from "items" from "items"
where id > ${+id} where id > ${+id} and active = 'true'
order by id order by id
limit 1 limit 1
`; `;
const prev = await db` const prev = await db`
select id select id
from "items" from "items"
where id < ${+id} where id < ${+id} and active = 'true'
order by id desc order by id desc
limit 1 limit 1
`; `;
@@ -103,7 +126,7 @@ export default router => {
const rows = db` const rows = db`
select id, mime, size, src, stamp, userchannel, username, usernetwork select id, mime, size, src, stamp, userchannel, username, usernetwork
from "items" from "items"
where username = ${user} where username = ${user} and active = 'true'
order by stamp desc order by stamp desc
limit ${+eps} limit ${+eps}
`; `;
@@ -203,13 +226,42 @@ export default router => {
msg: 'no postid' msg: 'no postid'
}); });
} }
const postid = +req.post.postid; const id = +req.post.postid;
await db` if(id <= 1) {
delete from "items" return res.json({
where id = ${+postid} success: false
});
}
const f0ck = await db`
select dest, mime
from "items"
where
id = ${id} and
active = 'true'
limit 1
`; `;
if(f0ck.length === 0) {
return res.json({
success: false,
msg: `f0ck ${id}: f0ck not found`
});
}
await db`update "items" set active = 'false' where id = ${id}`;
await fs.copyFile(`./public/b/${f0ck[0].dest}`, `./deleted/b/${f0ck[0].dest}`).catch(_=>{});
await fs.copyFile(`./public/t/${id}.webp`, `./deleted/t/${id}.webp`).catch(_=>{});
await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(_=>{});
await fs.unlink(`./public/t/${id}.webp`).catch(_=>{});
if(f0ck[0].mime.startsWith('audio')) {
await fs.copyFile(`./public/ca/${id}.webp`, `./deleted/ca/${id}.webp`).catch(_=>{});
await fs.unlink(`./public/ca/${id}.webp`).catch(_=>{});
}
res.json({ res.json({
success: true success: true
}); });
+1 -1
View File
@@ -16,7 +16,7 @@ export default router => {
const itemid = (await db` const itemid = (await db`
select id select id
from "items" from "items"
where id = ${+avatar} where id = ${+avatar} and active = 'true'
`)?.[0]?.id; `)?.[0]?.id;
if(!itemid) { if(!itemid) {
+9 -1
View File
@@ -30,7 +30,7 @@ export default router => {
const postid = +req.params.postid; const postid = +req.params.postid;
const tagname = req.post.tagname?.trim(); const tagname = req.post.tagname?.trim();
if(tagname.length >= 45) { if(tagname.length > 70) {
return res.json({ return res.json({
success: false, success: false,
msg: 'tag is too long!' msg: 'tag is too long!'
@@ -131,6 +131,14 @@ export default router => {
const postid = +req.params.postid; const postid = +req.params.postid;
const tagname = decodeURIComponent(req.params.tagname); const tagname = decodeURIComponent(req.params.tagname);
if(tagname == 'sfw' || tagname == 'nsfw' || tagname == 'hentai' || tagname == 'audio') {
return res.json({
success: false,
msg: 'blacklisted',
tags: await lib.getTags(postid)
});
}
const tags = await lib.getTags(postid); const tags = await lib.getTags(postid);
const tagid = tags.filter(t => t.tag === tagname)[0]?.id ?? null; const tagid = tags.filter(t => t.tag === tagname)[0]?.id ?? null;
+61 -42
View File
@@ -10,6 +10,65 @@ const auth = async (req, res, next) => {
}; };
export default (router, tpl) => { export default (router, tpl) => {
router.get(/\/user\/(?<user>.*)/, async (req, res) => {
const user = decodeURIComponent(req.params.user);
const query = await db`
select "user".user, "user".created_at, user_options.*
from user_options
left join "user" on "user".id = user_options.user_id
where "user".user ilike ${user}
limit 1
`;
if(!query.length) {
return res.reply({
code: 404,
body: tpl.render('error', {
message: 'this user does not exists',
tmp: null
}, req)
});
}
const f0cks = await f0cklib.getf0cks({
user: user,
mode: req.session.mode,
fav: false,
session: !!req.session
});
const favs = await f0cklib.getf0cks({
user: user,
mode: req.session.mode,
fav: true,
session: !!req.session
});
const count = {
f0cks: 0,
favs: 0
};
if('items' in f0cks) {
count.f0cks = f0cks.items.length;
f0cks.items = f0cks.items.slice(0, 50);
}
if('items' in favs) {
count.favs = favs.items.length;
favs.items = favs.items.slice(0, 50);
}
const data = {
user: query[0],
f0cks,
count,
favs,
tmp: null
};
return res.reply({ body: tpl.render('user', data, req) });
});
router.get(/^\/?(?:\/tag\/(?<tag>.+?))?(?:\/user\/(?<user>.+?)\/(?<mode>f0cks|favs))?(?:\/(?<mime>image|audio|video))?(?:\/p\/(?<page>\d+))?(?:\/(?<itemid>\d+))?$/, async (req, res) => { router.get(/^\/?(?:\/tag\/(?<tag>.+?))?(?:\/user\/(?<user>.+?)\/(?<mode>f0cks|favs))?(?:\/(?<mime>image|audio|video))?(?:\/p\/(?<page>\d+))?(?:\/(?<itemid>\d+))?$/, async (req, res) => {
const mode = req.params.itemid ? 'item' : 'index'; const mode = req.params.itemid ? 'item' : 'index';
const data = await (req.params.itemid ? f0cklib.getf0ck : f0cklib.getf0cks)({ const data = await (req.params.itemid ? f0cklib.getf0ck : f0cklib.getf0cks)({
@@ -19,7 +78,8 @@ export default (router, tpl) => {
page: req.params.page, page: req.params.page,
itemid: req.params.itemid, itemid: req.params.itemid,
fav: req.params.mode == 'favs', fav: req.params.mode == 'favs',
mode: req.session.mode mode: req.session.mode,
session: !!req.session
}); });
if(!data.success) { if(!data.success) {
return res.reply({ return res.reply({
@@ -68,46 +128,5 @@ export default (router, tpl) => {
res.redirect(`/${referer}`); res.redirect(`/${referer}`);
}); });
router.get(/^\/ranking$/, async (req, res) => {
try {
const list = await db`
select
"user".user,
coalesce("user_options".avatar, 47319) as avatar,
count(distinct(tag_id, item_id)) as count
from "tags_assign"
left join "user" on "user".id = "tags_assign".user_id
left join "user_options" on "user_options".user_id = "user".id
group by "user".user, "user_options".avatar
order by count desc
`;
const stats = await lib.countf0cks();
const hoster = await db`
with t as (
select
split_part(substring(src, position('//' in src)+2), '/', 1) part
from items
)
select t.part, count(t.part) as c
from t
group by t.part
order by c desc
limit 20
`;
res.reply({
body: tpl.render('ranking', {
list,
stats,
hoster,
tmp: null
}, req)
});
} catch(err) {
res.end(JSON.stringify(err.message));
}
});
return router; return router;
}; };
+6 -4
View File
@@ -1,3 +1,4 @@
import cfg from "../../inc/config.mjs";
import f0cklib from "../routeinc/f0cklib.mjs"; import f0cklib from "../routeinc/f0cklib.mjs";
export default (router, tpl) => { export default (router, tpl) => {
@@ -5,8 +6,8 @@ export default (router, tpl) => {
let referer = req.headers.referer ?? ''; let referer = req.headers.referer ?? '';
let opts = {}; let opts = {};
if(referer.match(/f0ck\.me/)) { // parse referer if(referer.match(new RegExp(cfg.main.url.regex))) { // parse referer
referer = referer.split("f0ck.me")[1]; referer = referer.split(cfg.main.url.domain)[1];
const tmp = referer.match(/^\/?(?:\/tag\/(?<tag>.+?))?(?:\/user\/(?<user>.+?)\/(?<mode>f0cks|favs))?(?:\/(?<mime>image|audio|video))?(?:\/p\/(?<page>\d+))?(?:\/(?<itemid>\d+))?$/); const tmp = referer.match(/^\/?(?:\/tag\/(?<tag>.+?))?(?:\/user\/(?<user>.+?)\/(?<mode>f0cks|favs))?(?:\/(?<mime>image|audio|video))?(?:\/p\/(?<page>\d+))?(?:\/(?<itemid>\d+))?$/);
if(tmp) if(tmp)
opts = tmp.groups; opts = tmp.groups;
@@ -18,7 +19,8 @@ export default (router, tpl) => {
mime: opts.mime, mime: opts.mime,
page: opts.page, page: opts.page,
fav: opts.mode == 'favs', fav: opts.mode == 'favs',
mode: req.session.mode mode: req.session.mode,
session: !!req.session
}); });
if(!data.success) { if(!data.success) {
@@ -31,7 +33,7 @@ export default (router, tpl) => {
}); });
} }
res.redirect(`/${data.link}${data.link.length != 0 ? "/" : ""}${data.itemid}`); res.redirect(`${data.link.main}${data.link.path}${data.itemid}`);
}); });
return router; return router;
}; };
+97
View File
@@ -0,0 +1,97 @@
import db from "../../inc/sql.mjs";
import lib from "../lib.mjs";
import config from "../config.mjs";
import fetch from "flumm-fetch";
export default (router, tpl) => {
router.get(/^\/ranking$/, async (req, res) => {
try {
const list = await db`
select
"user".user,
coalesce("user_options".avatar, ${await lib.getDefaultAvatar()}) as avatar,
count(distinct(tag_id, item_id)) as count
from "tags_assign"
left join "user" on "user".id = "tags_assign".user_id
left join "user_options" on "user_options".user_id = "user".id
group by "user".user, "user_options".avatar
order by count desc
`;
const stats = await lib.countf0cks();
const hoster = await db`
with t as (
select
split_part(substring(src, position('//' in src)+2), '/', 1) part
from items
)
select t.part, count(t.part) as c
from t
group by t.part
order by c desc
limit 20
`;
const favotop = await db`
select item_id, count(*) favs
from favorites
group by item_id
having count(*) > 1
order by favs desc
limit 10
`;
res.reply({
body: tpl.render('ranking', {
list,
stats,
hoster,
favotop,
tmp: null
}, req)
});
} catch(err) {
res.end(JSON.stringify(err.message));
}
});
router.get(/^\/top10$/, async (req, res) => {
const d = new Date();
d.setMonth(d.getMonth() - 1);
const month = (d.getMonth() + 1).toString().padStart(2, '0');
const year = d.getFullYear();
const url = `${config.apis.stats.url1}/${year}-${month}/${config.apis.stats.url2}`;
const options = {
method: 'GET',
headers: {
Authorization: config.apis.stats.auth
}
};
const topres = await (await fetch(url, options)).text();
const list = topres.match(/(f0ck.me\/b\/)(?<link>.{8}\.\w*)/g).slice(0, 10).map(e => e.split('/')[2]);
const f0cks = [];
for(const l of list) {
const f = await db`
select id, username
from items
where dest = ${l}
limit 1
`;
f0cks.push(f[0]);
}
res.reply({
body: tpl.render('top10', {
f0cks,
year,
month,
tmp: null
}, req)
});
});
return router;
};
+8 -5
View File
@@ -17,8 +17,8 @@ export default (router, tpl) => {
total = (await db` total = (await db`
select count(*) as total select count(*) as total
from "items" from "items"
where src ilike ${'%' + tag.substring(4) + '%'} where src ilike ${'%' + tag.substring(4) + '%'} and active = 'true'
group by "items".id, "tags".tag group by "items".id
`).length; `).length;
} }
else { else {
@@ -40,8 +40,11 @@ export default (router, tpl) => {
ret = await db` ret = await db`
select * select *
from "items" from "items"
where src ilike ${'%' + tag.substring(4) + '%'} where
group by "items".id, "tags".tag src ilike ${'%' + tag.substring(4) + '%'} and
active = 'true'
group by "items".id
order by "items".id desc
offset ${offset} offset ${offset}
limit ${_eps} limit ${_eps}
`; `;
@@ -52,7 +55,7 @@ export default (router, tpl) => {
from "tags" from "tags"
left join "tags_assign" on "tags_assign".tag_id = "tags".id left join "tags_assign" on "tags_assign".tag_id = "tags".id
left join "items" on "items".id = "tags_assign".item_id left join "items" on "items".id = "tags_assign".item_id
where "tags".tag ilike ${'%' + tag + '%'} where "tags".tag ilike ${'%' + tag + '%'} and "items".active = 'true'
group by "items".id, "tags".tag group by "items".id, "tags".tag
offset ${offset} offset ${offset}
limit ${_eps} limit ${_eps}
+14
View File
@@ -12,5 +12,19 @@ export default (router, tpl) => {
"Location": req.headers.referer ?? "/" "Location": req.headers.referer ?? "/"
}).end(); }).end();
}); });
router.get(/^\/tfull\//, async (req, res) => {
let full = req.session.fullscreen;
if(full == 1)
full = 0;
else
full = 1;
return res.writeHead(301, {
"Cache-Control": "no-cache, public",
"Set-Cookie": `theme=${full}; Path=/`,
"Location": req.headers.referer ?? "/"
}).end();
});
return router; return router;
}; };
-1
View File
@@ -1,4 +1,3 @@
//import knex from "knex";
import postgres from "postgres"; import postgres from "postgres";
import cfg from "./config.mjs"; import cfg from "./config.mjs";
+11 -7
View File
@@ -1,4 +1,5 @@
import { getLevel } from "../admin.mjs"; import { getLevel } from "../admin.mjs";
import lib from "../lib.mjs";
import fetch from "flumm-fetch"; import fetch from "flumm-fetch";
import vm from "vm"; import vm from "vm";
@@ -7,7 +8,9 @@ let context = vm.createContext({
e: null, e: null,
bot: null, bot: null,
admins: null, admins: null,
fetch: fetch, fetch,
lib,
console,
a: null, a: null,
resolve: null resolve: null
@@ -19,16 +22,16 @@ export default async bot => {
name: "level", name: "level",
call: /^!level (.*)/i, call: /^!level (.*)/i,
active: true, active: true,
f: e => { f: async e => {
const user = e.message.trim().substring(7); const user = e.message.trim().substring(7);
e.reply( JSON.stringify( getLevel( e.self.user.get(user) || {} ) ) ); await e.reply( JSON.stringify( getLevel( e.self.user.get(user) || {} ) ) );
} }
}, { }, {
name: "self", name: "self",
call: /^!self$/i, call: /^!self$/i,
active: true, active: true,
f: e => { f: async e => {
e.reply( JSON.stringify( e.user ) ); await e.reply( JSON.stringify( e.user ) );
} }
}, { }, {
name: "sandbox_debug", name: "sandbox_debug",
@@ -41,6 +44,7 @@ export default async bot => {
context.e = e; context.e = e;
context.bot = bot; context.bot = bot;
context.level = getLevel; context.level = getLevel;
context.hasTag = lib.hasTag;
context.a = null; context.a = null;
await new Promise(resolve => { await new Promise(resolve => {
@@ -52,9 +56,9 @@ export default async bot => {
let output = JSON.stringify(context.a); let output = JSON.stringify(context.a);
if(output.length > maxoutput) if(output.length > maxoutput)
return e.reply(`fuggg, Ausgabe wäre viel zu lang! (${output.length} Zeichen :DDDDDD)`); return await e.reply(`fuggg, Ausgabe wäre viel zu lang! (${output.length} Zeichen :DDDDDD)`);
else else
return e.reply(output); return await e.reply(output);
} }
}]; }];
}; };
+86 -18
View File
@@ -1,5 +1,6 @@
import { promises as fs } from "fs"; import { promises as fs } from "fs";
import db from "../sql.mjs"; import db from "../sql.mjs";
import { getLevel } from "../../inc/admin.mjs";
export default async bot => { export default async bot => {
@@ -7,37 +8,104 @@ export default async bot => {
name: "delete", name: "delete",
call: /^\!(del|rm) .*/i, call: /^\!(del|rm) .*/i,
active: true, active: true,
level: 100,
f: async e => { f: async e => {
const ret = (await Promise.all(e.args.map(async id => { let deleted = [];
for(let id of e.args) {
id = +id; id = +id;
if(id <= 0) if(id <= 1)
return false; continue;
const f0ck = await db` const f0ck = await db`
select dest select dest, mime, username, userchannel, usernetwork
from "items" from "items"
where id = ${+id} where
id = ${id} and
active = 'true'
limit 1 limit 1
`; `;
if(f0ck.length === 0) const level = getLevel(e.user).level;
return false;
if(f0ck.length === 0) {
await e.reply(`f0ck ${id}: f0ck not found`);
continue;
}
if(
(f0ck[0].username !== (e.user.nick || e.user.username) ||
f0ck[0].userchannel !== e.channel ||
f0ck[0].usernetwork !== e.network) &&
level < 100
) {
await e.reply(`f0ck ${id}: insufficient permissions`);
continue;
}
if(~~(new Date() / 1e3) >= (f0ck[0].stamp + 600) && level < 100) {
await e.reply(`f0ck ${id}: too late lol`);
continue;
}
await db`update "items" set active = 'false' where id = ${id}`;
await fs.copyFile(`./public/b/${f0ck[0].dest}`, `./deleted/b/${f0ck[0].dest}`).catch(_=>{});
await fs.copyFile(`./public/t/${id}.webp`, `./deleted/t/${id}.webp`).catch(_=>{});
await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(_=>{}); await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(_=>{});
await fs.unlink(`./public/t/${id}`).catch(_=>{}); await fs.unlink(`./public/t/${id}.webp`).catch(_=>{});
await db` if(f0ck[0].mime.startsWith('audio')) {
delete from "items" await fs.copyFile(`./public/ca/${id}.webp`, `./deleted/ca/${id}.webp`).catch(_=>{});
where id = ${+id} await fs.unlink(`./public/ca/${id}.webp`).catch(_=>{});
}
deleted.push(id);
}
await e.reply(`deleted ${deleted.length}/${e.args.length} f0cks (${deleted.join(",")})`);
}
}, {
name: "recover",
call: /^\!(recover) .*/i,
active: true,
level: 100,
f: async e => {
let recovered = [];
for(let id of e.args) {
id = +id;
if(id <= 1)
continue;
const f0ck = await db`
select dest, mime
from "items"
where
id = ${id} and
active = 'false'
limit 1
`; `;
return id; if(f0ck.length === 0) {
}))).filter(d => d); await e.reply(`f0ck ${id}: f0ck not found`);
continue;
}
if(ret.length > 0) await fs.copyFile(`./deleted/b/${f0ck[0].dest}`, `./public/b/${f0ck[0].dest}`).catch(_=>{});
e.reply(`deleted ${ret.length}/${e.args.length} (${ret.join(",")}) f0cks`); await fs.copyFile(`./deleted/t/${id}.webp`, `./public/t/${id}.webp`).catch(_=>{});
else await fs.unlink(`./deleted/b/${f0ck[0].dest}`).catch(_=>{});
e.reply(`oof`); await fs.unlink(`./deleted/t/${id}.webp`).catch(_=>{});
if(f0ck[0].mime.startsWith('audio')) {
await fs.copyFile(`./deleted/ca/${id}.webp`, `./public/ca/${id}.webp`).catch(_=>{});
await fs.unlink(`./deleted/ca/${id}.webp`).catch(_=>{});
}
await db`update "items" set active = 'true' where id = ${id}`;
recovered.push(id);
}
await e.reply(`recovered ${recovered.length}/${e.args.length} f0cks (${recovered.join(",")})`);
} }
}] }]
}; };
+16 -43
View File
@@ -5,29 +5,6 @@ import cfg from "../config.mjs";
import db from "../sql.mjs"; import db from "../sql.mjs";
import lib from "../lib.mjs"; import lib from "../lib.mjs";
/*const cleanTags = async () => {
const tags = await db`
select *
from "tags"
left join "tags_assign" on "tags_assign".tag_id = "tags".id
where "tags_assign".item_id is null
`;
if(tags.length === 0)
return 0;
let deleteTag = sql("tags");
let dtags = 0;
tags.forEach(tag => {
if(["sfw", "nsfw"].includes(tag.tag.toLowerCase()))
return dtags;
deleteTag = deleteTag.orWhere("id", tag.id);
dtags++;
});
await deleteTag.del();
return dtags;
};*/
export default async bot => { export default async bot => {
return [{ return [{
@@ -48,60 +25,56 @@ export default async bot => {
t: lib.formatSize((await Promise.all(dirs.t.map( async file => (await fs.stat(`./public/t/${file}`)).size)) ).reduce((a, b) => b + a)), t: lib.formatSize((await Promise.all(dirs.t.map( async file => (await fs.stat(`./public/t/${file}`)).size)) ).reduce((a, b) => b + a)),
ca: lib.formatSize((await Promise.all(dirs.ca.map(async file => (await fs.stat(`./public/ca/${file}`)).size))).reduce((a, b) => b + a)), ca: lib.formatSize((await Promise.all(dirs.ca.map(async file => (await fs.stat(`./public/ca/${file}`)).size))).reduce((a, b) => b + a)),
}; };
return e.reply(`${dirs.b.length} f0cks: ${sizes.b}, ${dirs.t.length} thumbnails: ${sizes.t}, ${dirs.ca.length} coverarts: ${sizes.ca}`); return await e.reply(`${dirs.b.length} f0cks: ${sizes.b}, ${dirs.t.length} thumbnails: ${sizes.t}, ${dirs.ca.length} coverarts: ${sizes.ca}`);
case "limit": case "limit":
return e.reply(`up to ${lib.formatSize(cfg.main.maxfilesize)} (${lib.formatSize(cfg.main.maxfilesize * cfg.main.adminmultiplier)} for admins)`); return await e.reply(`up to ${lib.formatSize(cfg.main.maxfilesize)} (${lib.formatSize(cfg.main.maxfilesize * cfg.main.adminmultiplier)} for admins)`);
case "thumb": case "thumb":
const rows = await db` const rows = await db`
select id select id
from "items" from "items"
`; `;
const dir = (await fs.readdir("./public/t")).filter(d => d.endsWith(".png")).map(e => +e.split(".")[0]); const dir = (await fs.readdir("./public/t")).filter(d => d.endsWith(".webp")).map(e => +e.split(".")[0]);
const tmp = []; const tmp = [];
for(let row of rows) for(let row of rows)
!dir.includes(row.id) ? tmp.push(row.id) : null; !dir.includes(row.id) ? tmp.push(row.id) : null;
e.reply(`${tmp.length}, ${rows.length}, ${dir.length}`); await e.reply(`${tmp.length}, ${rows.length}, ${dir.length}`);
break; break;
case "cache": case "cache":
cfg.websrv.cache = !cfg.websrv.cache; cfg.websrv.cache = !cfg.websrv.cache;
return e.reply(`Cache is ${cfg.websrv.cache ? "enabled" : "disabled"}`); return await e.reply(`Cache is ${cfg.websrv.cache ? "enabled" : "disabled"}`);
case "uptime": case "uptime":
exec('sudo systemctl status f0ck', (err, stdout) => { exec('sudo systemctl status f0ck', async (err, stdout) => {
if(!err) if(!err)
return e.reply(stdout.split('\n')[2].trim().replace("Active: active (running)", "i'm active")); return await e.reply(stdout.split('\n')[2].trim().replace("Active: active (running)", "i'm active"));
}); });
break; break;
case "restart": case "restart":
e.reply("hay hay patron, hemen!"); await e.reply("hay hay patron, hemen!");
exec("sudo systemctl restart f0ck"); exec("sudo systemctl restart f0ck");
break; break;
/*case "cleanTags":
const tags = await cleanTags();
e.reply(tags + " tags removed");
break;*/
case "clearTmp": case "clearTmp":
await Promise.all((await fs.readdir("./tmp")).filter(d => d !== ".empty").map(async d => fs.unlink(`./tmp/${d}`))); await Promise.all((await fs.readdir("./tmp")).filter(d => d !== ".empty").map(async d => fs.unlink(`./tmp/${d}`)));
e.reply("cleared lol"); await e.reply("cleared lol");
break; break;
case "status": case "status":
const tmpc = await lib.countf0cks(); const tmpc = await lib.countf0cks();
e.reply(`tagged: ${tmpc.tagged}; untagged: ${tmpc.untagged}; sfw: ${tmpc.sfw}; nsfw: ${tmpc.nsfw}; total: ${tmpc.total}`); await e.reply(`tagged: ${tmpc.tagged}; untagged: ${tmpc.untagged}; sfw: ${tmpc.sfw}; nsfw: ${tmpc.nsfw}; total: ${tmpc.total}`);
break; break;
case "autotagger": /*case "autotagger":
const body = { headers: { Authorization: `Basic ${cfg.tagger.btoa}` } }; const body = { headers: { Authorization: `Basic ${cfg.tagger.btoa}` } };
const res = await (await fetch(`${cfg.tagger.endpoint}/usage`, body)).json(); const res = await (await fetch(`${cfg.tagger.endpoint}/usage`, body)).json();
if(res) { if(res) {
const processed = res.result.monthly_processed; const processed = res.result.monthly_processed;
const limit = res.result.monthly_limit; const limit = res.result.monthly_limit;
return e.reply(`autotagger: usage/limit: ${processed}/${limit}`); return await e.reply(`autotagger: usage/limit: ${processed}/${limit}`);
} }
return; return;
break; break;*/
/*case "renameTag": /*case "renameTag":
const origTag = e.args.slice(1).join(' '); const origTag = e.args.slice(1).join(' ');
if(origTag.length <= 1) if(origTag.length <= 1)
return e.reply("absichtliche Provokation!"); return await e.reply("absichtliche Provokation!");
const origTagID = (await sql('tags').where('tag', origTag))[0].id; const origTagID = (await sql('tags').where('tag', origTag))[0].id;
@@ -116,10 +89,10 @@ export default async bot => {
.del() .del()
); );
e.reply(JSON.stringify({ affected, deleted })); await e.reply(JSON.stringify({ affected, deleted }));
break;*/ break;*/
case "help": case "help":
e.reply("cmds: stats, limit, thumb, cache, uptime, restart, cleanTags, clearTmp, status"); await e.reply("cmds: stats, limit, thumb, cache, uptime, restart, cleanTags, clearTmp, status");
break; break;
default: default:
return; return;
+3 -3
View File
@@ -22,10 +22,10 @@ export default async bot => {
`; `;
if(rows.length === 0) if(rows.length === 0)
return e.reply("no f0cks given! lol D:"); return await e.reply("no f0cks given! lol D:");
e.reply([ await e.reply([
`${cfg.main.url}/${rows[0].id}`, `${cfg.main.url.full}/${rows[0].id}`,
`user: ${rows[0].username} @ ${rows[0].usernetwork} ${rows[0].userchannel}`, `user: ${rows[0].username} @ ${rows[0].usernetwork} ${rows[0].userchannel}`,
`~${lib.formatSize(rows[0].size)}`, `~${lib.formatSize(rows[0].size)}`,
rows[0].mime, rows[0].mime,
+15 -3
View File
@@ -10,6 +10,7 @@ export default async bot => {
active: false, active: false,
f: async e => { f: async e => {
let args = e.args.slice(1); let args = e.args.slice(1);
/*let rows = sql("items").select("id", "username", "mime", "size"); /*let rows = sql("items").select("id", "username", "mime", "size");
for(let i = 0; i < args.length; i++) { for(let i = 0; i < args.length; i++) {
@@ -21,10 +22,21 @@ export default async bot => {
rows = await rows.orderByRaw("random()").limit(1);*/ rows = await rows.orderByRaw("random()").limit(1);*/
if(rows.length === 0) const rows = await db`
return e.reply("nothing found, f0cker"); select id, mime, username, size
from "items"
where
${ args.map(a => a.charAt(0) === "!"
? db`username not ilike ${a.slice(1)}`
: db`username ilike ${a}`
).join(' and ')}
order by random()
`;
return e.reply(`f0ckrnd: ${cfg.main.url}/${rows[0].id} by: ${rows[0].username} (${rows[0].mime}, ~${lib.formatSize(rows[0].size)})`); if(rows.length === 0)
return await e.reply("nothing found, f0cker");
return await e.reply(`f0ckrnd: ${cfg.main.url.full}/${rows[0].id} by: ${rows[0].username} (${rows[0].mime}, ~${lib.formatSize(rows[0].size)})`);
} }
}]; }];
}; };
+205 -167
View File
@@ -2,133 +2,223 @@ import cfg from "../config.mjs";
import db from "../sql.mjs"; import db from "../sql.mjs";
import lib from "../lib.mjs"; import lib from "../lib.mjs";
import { getLevel } from "../admin.mjs"; import { getLevel } from "../admin.mjs";
import queue from "../queue.mjs";
import autotagger from "../autotagger.mjs";
import fetch from "flumm-fetch"; import fetch from "flumm-fetch";
import fs from "fs"; import fs from "fs";
import { exec as _exec } from "child_process"; import path from "path";
const exec = cmd => new Promise((resolve, reject) => { const regex = {
_exec(cmd, { maxBuffer: 5e3 * 1024 }, (err, stdout, stderr) => { all: /https?:\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/gi,
if(err) yt: /(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\/?\?(?:\S*?&?v\=))|youtu\.be\/)([a-zA-Z0-9_-]{6,11})/gi,
return reject(err); imgur: /(?:https?:)?\/\/(\w+\.)?imgur\.com\/(\S*)(\.[a-zA-Z]{3})/gm,
if(stderr) instagram: /(?:https?:\/\/www\.)?instagram\.com\S*?\/(?:p|reel)\/(\w{11})\/?/im
console.error(stderr); };
resolve({ stdout: stdout }); const mediagroupids = new Set();
});
});
//const regex = /https?:\/\/[\w\S(\.|:|/)]+/gi;
const regex = /https?:\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/gi;
export default async bot => { export default async bot => {
return [{ return [{
name: "parser", name: "parser",
call: regex, call: regex.all,
active: true, active: true,
f: e => { f: e => {
const links = e.message.match(regex)?.filter(link => !link.includes("f0ck.me")) || []; const links = e.message.match(regex.all)?.filter(link => !link.includes(cfg.main.url.domain)) || [];
let repost;
if(e.photo) if(e.media)
links.push(e.photo); links.push(e.media);
if(links.length === 0) if(links.length === 0)
return false; return false;
if(e.message.match(/(!|-)ignore/)) if(e.message.match(/\!i(gnore)?\b/))
return false; return false;
if(!e.channel.includes("f0ck") && !e.message.match(/(!|-)f0ck/i)) if(!e.channel.includes("f0ck") && (!e.message.match(/\!f(0ck)?\b/i) && (typeof e.raw.forward_from == 'undefined')))
return false; return false;
if(e.type === 'tg' && // proto: tg
!e.message.match(/\!f(0ck)?\b/i) && // !f / !f0ck
!e.raw.forward_date && // is forwarded?
!mediagroupids.has(e.raw.media_group_id) // prepared mediagroup?
) {
return false;
}
else if(e.raw.media_group_id && e.message.match(/\!f(0ck)?\b/i)) {
mediagroupids.add(e.raw.media_group_id);
}
console.log(`parsing ${links.length} link${links.length > 1 ? "s" : ""}...`); console.log(`parsing ${links.length} link${links.length > 1 ? "s" : ""}...`);
links.forEach(async link => { links.forEach(async link => {
if(regex.imgur.test(link))
await e.reply(`imgur schmimigur`);
if(regex.instagram.test(link))
await e.reply(`insta schminsta`);
// check repost (link) // check repost (link)
const q_repost = await db` repost = await queue.checkrepostlink(link);
select id if(repost)
from "items" return await e.reply(`repost motherf0cker (link): ${cfg.main.url.full}/${repost}`);
where src = ${link}
`;
if(q_repost.length > 0)
return e.reply(`repost motherf0cker (link): ${cfg.main.url}/${q_repost[0].id}`);
// generate uuid // generate uuid
const uuid = (await db` const uuid = await queue.genuuid();
select gen_random_uuid() as uuid
`)[0].uuid.substring(0, 8);
const maxfilesize = (getLevel(e.user).level > 50 ? cfg.main.maxfilesize * cfg.main.adminmultiplier : cfg.main.maxfilesize) / 1024; const maxfilesize = (getLevel(e.user).level > 50 ? cfg.main.maxfilesize * cfg.main.adminmultiplier : cfg.main.maxfilesize);
let meta;
// read metadata // read metadata
let ext;
if(regex.imgur.test(link)) {
// is imgur
try { try {
meta = JSON.parse((await exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --skip-download --dump-json "${link}"`)).stdout); // will die extension von der url
} ext = link.split(".").slice(-1).join(".");
catch(err) { } catch(err) {
//e.reply("[error] f0ck has no bock :(");
//console.error(err);
return;
}
if(!Object.values(cfg.mimes).includes(meta.ext.toLowerCase())) {
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"]; const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
if(!Object.keys(cfg.mimes).includes(tmphead)) // this can be undefined for unsupported mime types, but will be caught in the general mime check below
return; ext = cfg.mimes[tmphead];
meta.ext = cfg.mimes[tmphead]; }
}
else {
// is not instagram
try {
const meta = JSON.parse((await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --skip-download --dump-json "${link}"`)).stdout);
ext = meta.ext;
} catch(err) {
const tmphead = (await fetch(link, { method: "HEAD" })).headers["content-type"];
// this can be undefined for unsupported mime types, but will be caught in the general mime check below
ext = cfg.mimes[tmphead];
}
} }
let filename = `${uuid}.${meta.ext}`; if(!Object.values(cfg.mimes).includes(ext?.toLowerCase())) {
return console.log('mime schmime ' + ext);
}
e.reply(`[charging the f0cker] downloading: ${uuid}`); const msg = await e.reply(`[charging the f0cker] downloading: ${uuid}`, {
disable_notification: true
});
// download data // download data
const start = new Date(); const start = new Date();
let source; let source;
if(meta.ext === "mp4") {
source = (await exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize}k --merge-output-format mp4 -o ./tmp/${filename}`)).stdout.trim(); try {
//change 720 to any other available resolution, higher = better quality but bigger filesize if(regex.instagram.test(link))
try {
// add --cookies <path-to-cookies-file> on local instance if you want to avoid getting rate limited
source = (await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize / 1024}k --postprocessor-args "ffmpeg:-bitexact" -o "./tmp/${uuid}.%(ext)s" --print after_move:filepath --merge-output-format "mp4"`)).stdout.trim();
} catch(err) {
if(e.type == 'tg')
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "instagram dl error");
return await e.reply("instagram dl error", err);
} }
else { else if(regex.imgur.test(link)) {
source = (await exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize}k -o ./tmp/${filename}`)).stdout.trim(); console.log("penis123");
//change 720 to any other available resolution, higher = better quality but bigger filesize try {
await queue.exec(`torsocks wget "${link}" -O "./tmp/${uuid}.${ext}"`);
source = "./tmp/"+uuid+"."+ext;
console.log(source);
} catch(err) {
console.log(err);
}
}
else
{
try {
source = (await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize / 1024}k --postprocessor-args "ffmpeg:-bitexact" -o "./tmp/${uuid}.%(ext)s" --print after_move:filepath --merge-output-format "mp4"`)).stdout.trim();
} catch(err) {
if(e.type == 'tg')
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, err);
return await e.reply(err);
}
}
} catch(err) {
console.log(err);
} }
if(source.match(/larger than/)) // this is how it was before I fucked it up :>
return e.reply("too large lol"); // try {
// source = (await queue.exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' "${link}" --max-filesize ${maxfilesize / 1024}k --postprocessor-args "ffmpeg:-bitexact" -o "./tmp/${uuid}.%(ext)s" --print after_move:filepath --merge-output-format "mp4"`)).stdout.trim();
// } catch(err) {
// if(e.type == 'tg')
// return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "something went wrong lol");
// return await e.reply("something went wrong lol");
// }
if(!source) {
if(e.type == 'tg')
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "something went wrong lol");
return await e.reply("something went wrong lol");
}
if(source.match(/larger than/)) {
if(e.type == 'tg')
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "too large lol");
return await e.reply("too large lol");
}
const end = ~~((new Date() - start) / 1e3); const end = ~~((new Date() - start) / 1e3);
// generate checksum // filesize check
const checksum = (await exec(`sha256sum ./tmp/${filename}`)).stdout.trim().split(" ")[0]; const size = fs.statSync(source).size;
const size = fs.statSync(`./tmp/${filename}`).size; if(size > maxfilesize) {
await fs.promises.unlink(source).catch(_=>{});
// mime check if(e.type == 'tg')
const mime = (await exec(`file --mime-type -b ./tmp/${filename}`)).stdout.trim(); return await e.editMessageText(msg.result.chat.id, msg.result.message_id, `too large lol. (${lib.formatSize(size)} / ${lib.formatSize(maxfilesize)})`);
if(!Object.keys(cfg.mimes).includes(mime)) return await e.reply(`too large lol. (${lib.formatSize(size)} / ${lib.formatSize(maxfilesize)})`);
return e.reply(`lol, go f0ck yourself (${mime})`);
if(!Object.values(cfg.mimes).includes(meta.ext.toLowerCase())) {
let tmpext = cfg.mimes[meta.ext.toLowerCase()];
fs.renameSync(`./tmp/${filename}`, `./tmp/${uuid}.${tmpext}`);
filename = `${uuid}.${tmpext}`;
} }
// check repost (checksum) // mime check
const q_repostc = await db` let mime = (await queue.exec(`file --mime-type -b ${source}`)).stdout.trim();
select id try {
from "items" if(mime == 'video/x-matroska') { // mkv failsafe
where checksum = ${checksum} await queue.exec(`ffmpeg -i ./tmp/${uuid}.mkv -codec copy ./tmp/${uuid}.mp4`);
`; await fs.promises.unlink(source).catch(_=>{});
if(q_repostc.length > 0) source = source.replace(/\.mkv$/, '.mp4');
return e.reply(`repost motherf0cker (checksum): ${cfg.main.url}/${q_repostc[0].id}`); mime = 'video/mp4';
}
if(source.match(/\.opus$/)) { // opus failsafe
await queue.exec(`ffmpeg -i ./tmp/${uuid}.opus -codec copy ./tmp/${uuid}.ogg`);
await fs.promises.unlink(source);
source = source.replace(/\.opus$/, '.ogg');
mime = 'audio/ogg';
}
} catch(err) {
await fs.promises.unlink(source).catch(_=>{});
if(e.type == 'tg')
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, "something went wrong lol");
return await e.reply("something went wrong lol");
}
await fs.promises.copyFile(`./tmp/${filename}`, `./public/b/${filename}`); if(!Object.keys(cfg.mimes).includes(mime)) {
await fs.promises.unlink(`./tmp/${filename}`).catch(_=>{}); await fs.promises.unlink(source).catch(_=>{});
if(e.type == 'tg')
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, `lol, go f0ck yourself (${mime})`);
return await e.reply(`lol, go f0ck yourself (${mime})`);
}
// generate checksum
const checksum = (await queue.exec(`sha256sum ${source}`)).stdout.trim().split(" ")[0];
// check repost (checksum)
repost = await queue.checkrepostsum(checksum);
if(repost) {
await fs.promises.unlink(source).catch(_=>{});
if(e.type == 'tg')
return await e.editMessageText(msg.result.chat.id, msg.result.message_id, `repost motherf0cker (checksum): ${cfg.main.url.full}/${repost}`);
return await e.reply(`repost motherf0cker (checksum): ${cfg.main.url.full}/${repost}`);
}
const filename = path.basename(source);
await fs.promises.copyFile(source, `./public/b/${filename}`);
await fs.promises.unlink(source).catch(_=>{});
await db` await db`
insert into items ${ insert into items ${
db({ db({
src: e.photo ? "" : link, src: e.media ? "" : link,
dest: filename, dest: filename,
mime: mime, mime: mime,
size: size, size: size,
@@ -137,54 +227,18 @@ export default async bot => {
userchannel: e.channel, userchannel: e.channel,
usernetwork: e.network, usernetwork: e.network,
stamp: ~~(new Date() / 1000), stamp: ~~(new Date() / 1000),
active: 1 active: true
}, 'src', 'dest', 'mime', 'size', 'checksum', 'username', 'userchannel', 'usernetwork', 'stamp', 'active') }, 'src', 'dest', 'mime', 'size', 'checksum', 'username', 'userchannel', 'usernetwork', 'stamp', 'active')
} }
`; `;
const itemid = (await db` const itemid = await queue.getItemID(filename);
select *
from "items"
where dest = ${filename}
limit 1
`)[0].id;
// generate thumbnail // generate thumbnail
try { try {
if(mime.startsWith('video/') || mime == 'image/gif') await queue.genThumbnail(filename, mime, itemid, link);
await exec(`ffmpegthumbnailer -i./public/b/${filename} -s1024 -o./tmp/${itemid}.png`);
else if(mime.startsWith('image/') && mime != 'image/gif')
await exec(`convert ./public/b/${filename} ./tmp/${itemid}.png`);
else if(mime.startsWith('audio/')) {
if(link.match(/soundcloud/)) {
let cover = (await exec(`yt-dlp -f 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w' --get-thumbnail "${link}"`)).stdout.trim();
if(!cover.match(/default_avatar/)) {
cover = cover.replace(/-(large|original)\./, '-t500x500.');
try {
await exec(`wget "${cover}" -O ./tmp/${itemid}.jpg`);
const size = (await fs.promises.stat(`./tmp/${itemid}.jpg`)).size;
if(size >= 0) {
await exec(`convert ./tmp/${itemid}.jpg ./tmp/${itemid}.png`);
await exec(`convert ./tmp/${itemid}.jpg ./public/ca/${itemid}.webp`);
}
} catch(err) {}
}
else {
await exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`);
await exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
}
}
else {
await exec(`ffmpeg -i ./public/b/${filename} -update 1 -map 0:v -map 0:1 -c copy ./tmp/${itemid}.png`);
await exec(`convert ./tmp/${itemid}.png ./public/ca/${itemid}.webp`);
}
}
await exec(`convert "./tmp/${itemid}.png" -resize "128x128^" -gravity center -crop 128x128+0+0 +repage ./public/t/${itemid}.webp`);
await fs.promises.unlink(`./tmp/${itemid}.png`).catch(err => {});
await fs.promises.unlink(`./tmp/${itemid}.jpg`).catch(err => {});
} catch(err) { } catch(err) {
await exec(`convert ./mugge.png ./public/t/${itemid}.webp`); await queue.exec(`convert ./mugge.png ./public/t/${itemid}.webp`);
} }
let speed = lib.calcSpeed(size, end); let speed = lib.calcSpeed(size, end);
@@ -192,58 +246,42 @@ export default async bot => {
// autotagger // autotagger
let tags = []; let tags = [];
try { /*if(cfg.apis?.nsfw1 && mime.startsWith('image')) {
if(mime.startsWith('image')) { const nsfw = await autotagger.isNSFW(filename, size);
const res = await lib.detectNSFW(filename); tags.push(nsfw ? 'nsfw' : 'sfw');
if(nsfw)
await queue.tagNSFW(itemid);
else
await queue.tagSFW(itemid);
}*/
await db` let outputmsgirc = `[f0cked] link: ${cfg.main.url.full}/${itemid} | size: ${lib.formatSize(size)} | speed: ${speed}`;
insert into "tags_assign" ${ let outputmsgtg = `[f0cked] size: ${lib.formatSize(size)} | speed: ${speed}`;
db({
item_id: itemid, if(tags?.length > 0) {
tag_id: res.nsfw ? 2 : 1, const tagstr = tags.join(', ');
user_id: 7 outputmsgirc += ` | tags: ${tagstr}`;
outputmsgtg += ` | tags: ${tagstr}`;
}
if(e.type == 'tg') {
await e.deleteMessage(msg.result.chat.id, msg.result.message_id);
await e.reply(outputmsgtg, {
reply_markup: JSON.stringify({
inline_keyboard: [[
{ text: (await lib.hasTag(itemid, 1) ? '✓ ' : '') + 'sfw', callback_data: `b_sfw:${itemid}` },
{ text: (await lib.hasTag(itemid, 2) ? '✓ ' : '') + 'nsfw', callback_data: `b_nsfw:${itemid}` },
{ text: 'tags', callback_data: `b_tags:${itemid}` },
{ text: '❌ delete', callback_data: `b_delete:${itemid}` }
], [
{ text: `open f0ck #${itemid}`, url: `${cfg.main.url.full}/${itemid}` }
]]
}) })
});
} }
`; else {
tags.push(res.nsfw ? 'nsfw' : 'sfw'); await e.reply(outputmsgirc);
if(res.hentai >= .7) {
await db`
insert into "tags_assign" ${
db({
item_id: f.id,
tag_id: 8, // hentai
user_id: 7 // autotagger
})
} }
`;
tags.push('hentai');
}
}
else if(mime.startsWith('audio')) {
await db`
insert into "tags_assign" ${
db([{
item_id: itemid,
tag_id: 1,
user_id: 7
}, {
item_id: itemid,
tag_id: 7178,
user_id: 7
}])
}
`;
tags.push('sfw', 'audio');
}
} catch(err) {
console.error(err);
}
e.reply([
`[f0cked] link: ${cfg.main.url}/${itemid} | size: ${lib.formatSize(size)} | speed: ${speed}` + (tags.length > 0 ? ` | tags: ${tags.join(', ')}` : '')
]);
}); });
} }
}]; }];
+12 -12
View File
@@ -12,11 +12,11 @@ export default async bot => {
f: async e => { f: async e => {
const id = +e.args[1]; const id = +e.args[1];
if(!id) if(!id)
return e.reply("lol no"); return await e.reply("lol no");
const tags = (await lib.getTags(id)).map(t => t.tag); const tags = (await lib.getTags(id)).map(t => t.tag);
if(tags.length === 0) if(tags.length === 0)
return e.reply(`item ${cfg.main.url}/${id} has no tags!`); return await e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
return e.reply(`item ${cfg.main.url}/${id} is tagged as: ${tags.join(', ')}`); return await e.reply(`item ${cfg.main.url.full}/${id} is tagged as: ${tags.join(', ')}`);
} }
}, { }, {
name: "tags add", name: "tags add",
@@ -26,7 +26,7 @@ export default async bot => {
f: async e => { f: async e => {
const id = +e.args[1]; const id = +e.args[1];
if(!id) if(!id)
return e.reply("lol no"); return await e.reply("lol no");
const tags = (await lib.getTags(id)).map(t => t.tag); const tags = (await lib.getTags(id)).map(t => t.tag);
@@ -35,7 +35,7 @@ export default async bot => {
: e.args.splice(2)).filter(t => !tags.includes(t) && t.length > 0); : e.args.splice(2)).filter(t => !tags.includes(t) && t.length > 0);
if(newtags.length === 0) if(newtags.length === 0)
return e.reply("no (new) tags provided"); return await e.reply("no (new) tags provided");
await Promise.all(newtags.map(async ntag => { await Promise.all(newtags.map(async ntag => {
try { try {
@@ -73,8 +73,8 @@ export default async bot => {
const ntags = (await lib.getTags(id)).map(t => t.tag); const ntags = (await lib.getTags(id)).map(t => t.tag);
if(ntags.length === 0) if(ntags.length === 0)
return e.reply(`item ${cfg.main.url}/${id} has no tags!`); return await e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
return e.reply(`item ${cfg.main.url}/${id} is now tagged as: ${ntags.join(', ')}`); return await e.reply(`item ${cfg.main.url.full}/${id} is now tagged as: ${ntags.join(', ')}`);
} }
}, { }, {
name: "tags remove", name: "tags remove",
@@ -84,7 +84,7 @@ export default async bot => {
f: async e => { f: async e => {
const id = +e.args[1]; const id = +e.args[1];
if(!id) if(!id)
return e.reply("lol no"); return await e.reply("lol no");
const tags = await lib.getTags(id); const tags = await lib.getTags(id);
@@ -93,7 +93,7 @@ export default async bot => {
: e.args.splice(2)).filter(t => t.length > 0); : e.args.splice(2)).filter(t => t.length > 0);
if(removetags.length === 0) if(removetags.length === 0)
return e.reply("no tags provided"); return await e.reply("no tags provided");
const res = await Promise.all(removetags.map(async rtag => { const res = await Promise.all(removetags.map(async rtag => {
const tagid = tags.filter(t => t.tag === rtag)[0]?.id ?? null; const tagid = tags.filter(t => t.tag === rtag)[0]?.id ?? null;
@@ -122,12 +122,12 @@ export default async bot => {
}; };
})); }));
e.reply(JSON.stringify(res)); await e.reply(JSON.stringify(res));
const ntags = (await lib.getTags(id)).map(t => t.tag); const ntags = (await lib.getTags(id)).map(t => t.tag);
if(ntags.length === 0) if(ntags.length === 0)
return e.reply(`item ${cfg.main.url}/${id} has no tags!`); return await e.reply(`item ${cfg.main.url.full}/${id} has no tags!`);
return e.reply(`item ${cfg.main.url}/${id} is now tagged as: ${ntags.join(', ')}`); return await e.reply(`item ${cfg.main.url.full}/${id} is now tagged as: ${ntags.join(', ')}`);
} }
}] }]
}; };
+42
View File
@@ -0,0 +1,42 @@
import queue from '../queue.mjs';
import db from '../sql.mjs';
export default async bot => {
return [{
name: "thumbnailer",
call: /^\!thumb .*/i,
active: true,
level: 100,
f: async e => {
let processed = [];
for(let id of e.args) {
id = +id;
if(id <= 1)
continue;
const f0ck = await db`
select id, dest, mime, src
from "items"
where
id = ${id} and
active = 'true'
limit 1
`;
if(f0ck.length === 0) {
await e.reply(`f0ck ${id}: f0ck not found`);
continue;
}
// gen thumb
await queue.genThumbnail(f0ck[0].dest, f0ck[0].mime, f0ck[0].id, f0ck[0].src);
processed.push(id);
}
return await e.reply(`thumbnails: ${processed.length}/${e.args.length} (${processed.join(",")})`);
}
}];
};
+14 -3
View File
@@ -5,6 +5,11 @@ import cuffeo from "cuffeo";
import { promises as fs } from "fs"; import { promises as fs } from "fs";
import flummpress from "flummpress"; import flummpress from "flummpress";
process.on('unhandledRejection', err => {
console.error(err);
throw err;
});
(async () => { (async () => {
const self = { const self = {
_trigger: new Map(), _trigger: new Map(),
@@ -61,7 +66,8 @@ import flummpress from "flummpress";
req.session = false; req.session = false;
if(req.url.pathname.match(/^\/(s|b|t|ca)\//)) if(req.url.pathname.match(/^\/(s|b|t|ca)\//))
return; return;
req.theme = req.cookies.theme ?? 'f0ck'; req.theme = req.cookies.theme || 'f0ck';
req.fullscreen = req.cookies.fullscreen || 0;
if(req.cookies.session) { if(req.cookies.session) {
const user = await db` const user = await db`
@@ -95,18 +101,23 @@ import flummpress from "flummpress";
where id = ${+user[0].sess_id} where id = ${+user[0].sess_id}
`; `;
req.session.theme = req.cookies.theme;
req.session.fullscreen = req.cookies.fullscreen;
// update userprofile // update userprofile
await db` await db`
insert into "user_options" ${ insert into "user_options" ${
db({ db({
user_id: +user[0].id, user_id: +user[0].id,
mode: user[0].mode ?? 0, mode: user[0].mode ?? 0,
theme: req.session.theme ?? 'f0ck' theme: req.session.theme ?? 'f0ck',
}, 'user_id', 'mode', 'theme') fullscreen: req.session.fullscreen || 0
}, 'user_id', 'mode', 'theme', 'fullscreen')
} }
on conflict ("user_id") do update set on conflict ("user_id") do update set
mode = excluded.mode, mode = excluded.mode,
theme = excluded.theme, theme = excluded.theme,
fullscreen = excluded.fullscreen,
user_id = excluded.user_id user_id = excluded.user_id
`; `;
} }
+5 -3
View File
@@ -1,7 +1,8 @@
@include(snippets/header) @include(snippets/header)
<div id="main">
<div class="about"> <div class="about">
<div> <div>
<a href="/48908"><img src="/s/img/loool.webp" /></a> <a href="//f0ck.me/48908"><img src="//f0ck.me/s/img/loool.webp" /></a>
<p>thanks to our turkish fellow lol@n0xy/#f0ck for this gif &lt;3</p> <p>thanks to our turkish fellow lol@n0xy/#f0ck for this gif &lt;3</p>
</div> </div>
<h5>f0ck Contact</h5> <h5>f0ck Contact</h5>
@@ -34,7 +35,7 @@
<li>admin@f0ck.me</li> <li>admin@f0ck.me</li>
</ul> </ul>
<h5>Compatibility</h5> <h5>Compatibility</h5>
<!--<p>f0ck is developed and tested for Firefox and Chromium in their latest versions</p>--> <p>f0ck is developed and tested for Firefox and Chromium in their latest versions</p>
<p>If you encounter bugs please report them so we can fix them.</p> <p>If you encounter bugs please report them so we can fix them.</p>
<p>Microsoft Edgy is not actively supported, but if it werks, great! Same for anything apple related.</p> <p>Microsoft Edgy is not actively supported, but if it werks, great! Same for anything apple related.</p>
<h5>Tinfoil f0ckers listen!</h5> <h5>Tinfoil f0ckers listen!</h5>
@@ -44,6 +45,7 @@
<p>f0ck is completely functional without javascript enabled, you can be the beardiest neckbeard of all, we got you m'gentleman, if you want to use a custom theme you gotta allow our style cookie.</p> <p>f0ck is completely functional without javascript enabled, you can be the beardiest neckbeard of all, we got you m'gentleman, if you want to use a custom theme you gotta allow our style cookie.</p>
<h5>f0ck Privacy?</h5> <h5>f0ck Privacy?</h5>
<p>Cookies: Yes, we set 1 cookie for your prefered stylesheet, this is a optional cookie and not required for the site to function, simply cosmetics, you can block this cookie and the site will still work as intended and will default to the default f0ck theme called "f0ck"</p> <p>Cookies: Yes, we set 1 cookie for your prefered stylesheet, this is a optional cookie and not required for the site to function, simply cosmetics, you can block this cookie and the site will still work as intended and will default to the default f0ck theme called "f0ck"</p>
<p>Logs: No for Tor - Yes for cloudflare and cloudflare probably sells your soul to the devil, however our webserver doesn't log cloudflare connecting to our webserver, if you want to lurk without being flared by the cloud, see the above tor section my man</p> <p>Logs: We do not log users accessing our website.</p>
</div>
</div> </div>
@include(snippets/footer) @include(snippets/footer)
+21 -2
View File
@@ -1,3 +1,22 @@
@include(snippets/header_admin) @include(snippets/header)
<div id="main">
<div class="container">
<h1>ADMINBEREICH</h1>
<h5>Hallo, {{ session.user }}</h5>
<span>Hier entsteht eine Internetpräsenz!</span><br>
<hr>
<p>f0ck stats: @if(typeof totals !== "undefined")
total: {{ totals.total }} | tagged: {{ totals.tagged }} | untagged: {{ totals.untagged }} | sfw: {{ totals.sfw }} | nsfw: {{ totals.nsfw }}
@endif</p>
<hr>
<div class="admintools">
<p>Adminwerkzeuge</p>
<ul>
<li><a href="/admin/log">Logs</a></li>
<li><a href="/admin/recover">Recover f0cks</a></li>
<li><a href="/admin/sessions">Sessions</a></li>
</ul>
</div>
</div>
</div>
@include(snippets/footer) @include(snippets/footer)
+3 -1
View File
@@ -1,4 +1,5 @@
@include(snippets/header_admin) @include(snippets/header)
<div id="main">
@if(log) @if(log)
<h1>last {{ log.length }} entries:</h1> <h1>last {{ log.length }} entries:</h1>
<div class="logwrap"> <div class="logwrap">
@@ -13,4 +14,5 @@
})(); })();
</script> </script>
@endif @endif
</div>
@include(snippets/footer) @include(snippets/footer)
+26
View File
@@ -0,0 +1,26 @@
@include(snippets/header)
<div id="main">
<table class="table" style="width: 100%">
<thead>
<tr>
<td></td>
<td>ID</td>
<td>f0cker</td>
<td>mime</td>
<td></td>
</tr>
</thead>
<tbody>
@each(posts as post)
<tr>
<td><img src="data:image/webp;base64,{{ post.thumbnail }}" /></td>
<td>{{ post.id }}</td>
<td>{{ post.username }}</td>
<td>{{ post.mime }}</td>
<td><a href="/admin/recover/?id={{ post.id }}">recover</a></td>
</tr>
@endeach
</tbody>
</table>
</div>
@include(snippets/footer)
+8 -2
View File
@@ -1,5 +1,7 @@
@include(snippets/header_admin) @include(snippets/header)
<table style="width: 100%;"> <div id="main">
<table class="table" style="width: 100%">
<thead>
<tr> <tr>
<td></td> <td></td>
<td>ID</td> <td>ID</td>
@@ -10,6 +12,8 @@
<td>last_used</td> <td>last_used</td>
<td>last_action</td> <td>last_action</td>
</tr> </tr>
</thead>
<tbody>
@each(sessions as session) @each(sessions as session)
<tr> <tr>
<td>{{ session.kmsi ? '&#9875;' : '' }}</td> <td>{{ session.kmsi ? '&#9875;' : '' }}</td>
@@ -22,5 +26,7 @@
<td>{{ session.last_action }}</td> <td>{{ session.last_action }}</td>
</tr> </tr>
@endeach @endeach
</tbody>
</table> </table>
</div>
@include(snippets/footer) @include(snippets/footer)
+14 -2
View File
@@ -1,8 +1,20 @@
@include(snippets/header) @include(snippets/header)
<div id="main">
<div class="container"> <div class="container">
<div class="_error_wrapper">
<div class="err"> <div class="err">
<span>{{ message }}</span> <div class="_error_topbar">
<img src="https://f0ck.me/s/img/favicon.gif" alt="f0ck?!"> <span>x.x</span>
</div>
<div class="_error_content">
<img src="/s/img/favicon.gif" alt="f0ck?!">
<div class="_error_message">
<span>Error</span>
<code>{{ message }}</code>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
@include(snippets/footer) @include(snippets/footer)
+5 -1
View File
@@ -1,8 +1,10 @@
@include(snippets/header) @include(snippets/header)
<div class="pagewrapper">
<div id="main">
<div class="index-container"> <div class="index-container">
@if(tmp.user)<h2>user: {!! tmp.user.toLowerCase() !!}@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif @if(tmp.user)<h2>user: {!! tmp.user.toLowerCase() !!}@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif
@if(tmp.tag)<h2>tag: @if(session)<a href="/search?tag={!! tmp.tag.toLowerCase() !!}" target="_blank">{!! tmp.tag.toLowerCase() !!}</a>@else{!! tmp.tag.toLowerCase() !!}@endif@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif @if(tmp.tag)<h2>tag: @if(session)<a href="/search?tag={!! tmp.tag.toLowerCase() !!}" target="_blank">{!! tmp.tag.toLowerCase() !!}</a>@else{!! tmp.tag.toLowerCase() !!}@endif@if(tmp.mime) ({{ tmp.mime }}s)@else (all)@endif</h2>@endif
<div id="posts"> <div class="posts">
@each(items as item) @each(items as item)
<a href="{{ link.main }}{{ item.id }}" data-mime="{{ item.mime }}" data-mode="{{ item.tag_id ? ['','sfw','nsfw'][item.tag_id] : 'null' }}" style="background-image: url('/t/{{ item.id }}.webp')"><p></p></a> <a href="{{ link.main }}{{ item.id }}" data-mime="{{ item.mime }}" data-mode="{{ item.tag_id ? ['','sfw','nsfw'][item.tag_id] : 'null' }}" style="background-image: url('/t/{{ item.id }}.webp')"><p></p></a>
@endeach @endeach
@@ -11,4 +13,6 @@
&#9660; &#9660;
</div> </div>
</div> </div>
</div>
</div>
@include(snippets/footer) @include(snippets/footer)
+22 -4
View File
@@ -1,5 +1,23 @@
@include(snippets/header) @include(snippets/header)
<canvas class="hidden-xs" id="bg"></canvas>
<div class="wrapper">
<div id="main">
<div class="container"> <div class="container">
<div class="_204863">
<div class="imageDoor" style="--hover-image: url('/t/{{ item.id }}.webp');">
<img src="/t/{{ item.id }}.webp" alt="" />
</div>
<div class="gapLeft">
<span class="populateME"><b>f0ck</b> - {{ item.id }}</span>
</div>
@if(session)
<div class="gapRight">
<svg class="iconset" id="a_favo"><use href="/s/img/iconset.svg#heart_{{ Object.values(item.favorites).filter(u => u.user == session.user)[0] ? 'solid' : 'regular' }}"></use></svg>
<svg class="iconset" id="a_tfull"><use href="/s/img/iconset.svg#window-{{ fullscreen == 1 ? 'minimize' : 'maximize' }}"></use></svg>
<svg class="iconset" id="a_delete"><use href="/s/img/iconset.svg#cross"></use></svg>
</div>
@endif
</div>
<div class="content"> <div class="content">
<div class="next-post"> <div class="next-post">
@if(pagination.prev) @if(pagination.prev)
@@ -45,11 +63,9 @@
</div> </div>
<div class="metadata"> <div class="metadata">
<span class="badge badge-dark"> <span class="badge badge-dark">
<a href="/{{ item.id }}" style="--hover-image: url('/t/{{ item.id }}.webp');" class="id-link">{{ item.id }}</a> <a href="/{{ item.id }}" class="id-link">{{ item.id }}</a>
@if(session) @if(session)
(<a id="a_username" href="/user/{{ user.name.toLowerCase() }}/f0cks@if(tmp.mime)/{{ tmp.mime }}@endif">{{ user.name }}</a>) (<a id="a_username" href="/user/{{ user.name.toLowerCase() }}/f0cks@if(tmp.mime)/{{ tmp.mime }}@endif">{{ user.name }}</a>)
<svg class="iconset" id="a_delete"><use href="/s/img/iconset.svg#cross"></use></svg>
<svg class="iconset" id="a_favo"><use href="/s/img/iconset.svg#heart_{{ Object.values(item.favorites).filter(u => u.user == session.user)[0] ? 'solid' : 'regular' }}"></use></svg>
@endif @endif
</span> </span>
<span class="badge badge-dark">{{ user.network }} / {{ user.channel }}</span> <span class="badge badge-dark">{{ user.network }} / {{ user.channel }}</span>
@@ -67,7 +83,7 @@
@if(typeof item.tags !== "undefined") @if(typeof item.tags !== "undefined")
@each(item.tags as tag) @each(item.tags as tag)
<span @if(session)tooltip="{{ tag.user }}"@endif class="badge {{ tag.badge }} mr-2"> <span @if(session)tooltip="{{ tag.user }}"@endif class="badge {{ tag.badge }} mr-2">
<a href="/tag/{{ tag.tag }}">{!! tag.tag !!}</a>@if(session)&nbsp;<a class="removetag" href="#">&#215;</a>@endif <a href="/tag/{{ tag.normalized }}">{!! tag.tag !!}</a>@if(session)&nbsp;<a class="removetag" href="#">&#215;</a>@endif
</span> </span>
@endeach @endeach
@endif @endif
@@ -85,4 +101,6 @@
</span> </span>
</div> </div>
</div> </div>
</div>
</div>
@include(snippets/footer) @include(snippets/footer)
+11
View File
@@ -1,4 +1,5 @@
@include(snippets/header) @include(snippets/header)
<div id="main">
<div class="topf0ckers"> <div class="topf0ckers">
<h3>Top f0ckers of all time <br>- Ranking -</h3> <h3>Top f0ckers of all time <br>- Ranking -</h3>
</div> </div>
@@ -34,7 +35,17 @@
<tr><td>untagged</td><td>{{ stats.untagged }}</td></tr> <tr><td>untagged</td><td>{{ stats.untagged }}</td></tr>
<tr><td>SFW</td><td>{{ stats.sfw }}</td></tr> <tr><td>SFW</td><td>{{ stats.sfw }}</td></tr>
<tr><td>NSFW</td><td>{{ stats.nsfw }}</td></tr> <tr><td>NSFW</td><td>{{ stats.nsfw }}</td></tr>
<tr><td>deleted</td><td>{{ stats.deleted }}</td></tr>
<tr><td>missing ids</td><td>{{ stats.untracked }}</td></tr>
</table>
<h3>Top f0cks</h3>
<table class="table">
@each(favotop as favo)
<tr><td><a href="/{{ favo.item_id }}">{{ favo.item_id }}</a></td><td>{{ favo.favs }}</td></tr>
@endeach
</table> </table>
</div> </div>
</div> </div>
</div>
@include(snippets/footer) @include(snippets/footer)
+20 -8
View File
@@ -1,11 +1,21 @@
@include(snippets/header) @include(snippets/header)
<h1 style="text-align: center">f0ckgle</h1> <div id="main">
<div class="f0ckgle">
<div class="search-title">
<span style="color:#4285F4;">f</span>
<span style="color:#EA4335;">0</span>
<span style="color:#FBBC05;">c</span>
<span style="color:#4285F4;">k</span>
<span style="color:#34A853;">g</span>
<span style="color:#EA4335;">l</span>
<span style="color:#4285F4;">e</span>
</div>
<form action="/search" class="admin-search"> <form action="/search" class="admin-search">
<input type="text" name="tag" value="{!! searchstring || '' !!}" /><button type="submit"><b>f0ck</b></button> <input type="text" name="tag" value="{!! searchstring || '' !!}" /><button type="submit">🔍</button>
</form> </form>
<div class="results"> <div class="results">
@if(result) @if(result)
<h1>{{ count }} f0cks given (page {{ pagination.page }} of {{ pagination.end }}):</h1> <h2>{{ count }} f0cks given (page {{ pagination.page }} of {{ pagination.end }}):</h2>
<table style="width: 100%" class="table"> <table style="width: 100%" class="table">
<thead> <thead>
<tr> <tr>
@@ -21,15 +31,17 @@
@each(result as line) @each(result as line)
<tr> <tr>
<td style="width: 128px;"><a href="/tag/{!! line.tag !!}/{{ line.id }}" target="_blank"><img src="/t/{{ line.id }}.webp" /></a></td> <td style="width: 128px;"><a href="/tag/{!! line.tag !!}/{{ line.id }}" target="_blank"><img src="/t/{{ line.id }}.webp" /></a></td>
<td><a href="/tag/{!! line.tag !!}/{{ line.id }}" target="_blank">{{ line.id }}</a></td> <td><span class="mview_desc">ID:</span><a href="/tag/{!! line.tag !!}/{{ line.id }}" target="_blank">{{ line.id }}</a></td>
<td><a href="/tag/{!! line.tag !!}">{!! line.tag !!}</a></td> <td><span class="mview_desc">Tag:</span><a href="/tag/{!! line.tag !!}">{!! line.tag !!}</a></td>
<td>{{ line.mime }}</td> <td><span class="mview_desc">Mime:</span>{{ line.mime }}</td>
<td><a href="/user/{!! line.username !!}/f0cks/{{ line.id }}">{!! line.username !!}</a></td> <td><span class="mview_desc">User:</span><a href="/user/{!! line.username !!}/f0cks/{{ line.id }}">{!! line.username !!}</a></td>
<td>{{ line.score.toFixed(2) }}</td> <td><span class="mview_desc">Score:</span>{{ line.score?.toFixed(2) }}</td>
</tr> </tr>
@endeach @endeach
</tbody> </tbody>
</table> </table>
@endif @endif
</div> </div>
</div>
</div>
@include(snippets/footer) @include(snippets/footer)
+3 -1
View File
@@ -1,6 +1,7 @@
@include(snippets/header) @include(snippets/header)
<div id="main">
<h1>Settings</h1> <h1>Settings</h1>
@if(session.avatar)<a href="//f0ck.me/{{ session.avatar }}"><img id="img_avatar" src="/t/{{ session.avatar }}.webp" /></a>@endif @if(session.avatar)<a href="/{{ session.avatar }}"><img id="img_avatar" src="/t/{{ session.avatar }}.webp" /></a>@endif
<h2>Account</h2> <h2>Account</h2>
<table class="table"> <table class="table">
<tbody> <tbody>
@@ -49,4 +50,5 @@
@endeach @endeach
</tbody> </tbody>
</table> </table>
</div>
@include(snippets/footer) @include(snippets/footer)
+1 -1
View File
@@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html lang="en" theme="@if(typeof theme !== "undefined"){{ theme }}@endif"> <html lang="en" theme="@if(typeof theme !== "undefined"){{ theme }}@endif" res="@if(typeof fullscreen !== "undefined"){{ fullscreen == 1 ? 'fullscreen' : '' }}@endif">
<head> <head>
<title>f0ck!</title> <title>f0ck!</title>
<meta name="description" content="Welcome to the internet"/> <meta name="description" content="Welcome to the internet"/>
-18
View File
@@ -1,18 +0,0 @@
<!doctype html>
<html lang="en" theme="@if(typeof theme !== "undefined"){{ theme }}@endif">
<head>
<title>@if(typeof data !== "undefined" && data.title){{ data.title }}@elsef0ck!@endif</title>
<link rel="icon" type="image/gif" href="/s/img/favicon.gif" />
<link rel="stylesheet" href="/s/css/f0ck.css">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="f0ck.me is the place where internet purists gather to celebrate content of all kinds">
@if(typeof data !== "undefined" && data.item)
<meta property="og:site_name" content="f0ck.me" />
<meta property="og:description"/>
<meta name="Description"/>
<meta property="og:image" content="{{ item.thumbnail }}" />
@endif
</head>
<body>
@include(snippets/navbar_admin)
+67 -16
View File
@@ -1,38 +1,32 @@
@if(session)
<nav class="navbar navbar-expand-lg"> <nav class="navbar navbar-expand-lg">
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a> <a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a>
<div class="navigation-links"> <div class="navigation-links">
<ul class="navbar-nav"> <ul class="navbar-nav">
<li class="nav-item dropdown"> <li class="nav-item dropdown">
@if(session) <a class="nav-link user" href="#" content="{{ session.user }}" data-toggle="dropdown">
<a class="nav-link" href="#" content="{{ session.user }}" data-toggle="dropdown"> <img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar" /><span>{{ session.user }}</span>
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar" />&nbsp;{{ session.user }}
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="/admin">adminpanel</a></li> <li><a href="/user/{{ session.user.toLowerCase() }}">my profile</a></li>
<li><a href="/user/{{ session.user.toLowerCase() }}/f0cks">my f0cks</a></li> <li><a href="/user/{{ session.user.toLowerCase() }}/f0cks">my f0cks</a></li>
<li><a href="/user/{{ session.user.toLowerCase() }}/favs">my favs</a></li> <li><a href="/user/{{ session.user.toLowerCase() }}/favs">my favs</a></li>
<li><a href="/settings">settings</a></li>
<li><a href="/search">search</a></li> <li><a href="/search">search</a></li>
<li><a href="/admin">Admin</a></li>
<li><a href="/about">About</a></li> <li><a href="/about">About</a></li>
<li><a href="/ranking">Ranking</a></li> <li><a href="/ranking">ranking</a></li>
<li><a href="/settings">settings</a></li>
<li><a href="/logout">logout</a></li> <li><a href="/logout">logout</a></li>
</ul> </ul>
@else
<a class="nav-link" href="/about" data-toggle="dropdown">About</a>
<ul class="dropdown-menu">
<li><a href="/login">login</a></li>
</ul>
@endif
</li> </li>
<li class="nav-item dropdown" id="themes"> <li class="nav-item dropdown" id="themes">
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Theme</a> <a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Themes</a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
@each(themes as t) @each(themes as t)
<li><a href="/theme/{{ t }}">{{ t }}</a></li> <li><a href="/theme/{{ t }}">{{ t }}</a></li>
@endeach @endeach
</ul> </ul>
</li> </li>
<span class="placeholder">&nbsp;</span>
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link ddcontent" href="#"@if(tmp?.mime) content="{{ tmp?.mime }}" data-toggle="dropdown"@endif>Filter@if(!tmp?.mime)&nbsp;&#9660;@endif</a> <a class="nav-link ddcontent" href="#"@if(tmp?.mime) content="{{ tmp?.mime }}" data-toggle="dropdown"@endif>Filter@if(!tmp?.mime)&nbsp;&#9660;@endif</a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
@@ -42,7 +36,6 @@
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifimage">Image</a></li> <li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifimage">Image</a></li>
</ul> </ul>
</li> </li>
@if(session)
<li class="nav-item @if(session)dropdown@endif"> <li class="nav-item @if(session)dropdown@endif">
<a class="nav-link ddcontent" href="#"@if(typeof session.mode !== "undefined") content="{{ modes[session.mode] ?? 'sfw' }}" data-toggle="dropdown"@endif>Mode</a> <a class="nav-link ddcontent" href="#"@if(typeof session.mode !== "undefined") content="{{ modes[session.mode] ?? 'sfw' }}" data-toggle="dropdown"@endif>Mode</a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
@@ -51,7 +44,6 @@
@endfor @endfor
</ul> </ul>
</li> </li>
@endif
<li class="nav-item"> <li class="nav-item">
<a id="random" class="nav-link" href="/random"> <a id="random" class="nav-link" href="/random">
<span class="nav-link-identifier">Random</span> <span class="nav-link-identifier">Random</span>
@@ -81,3 +73,62 @@
</div> </div>
</div> </div>
</nav> </nav>
@else
<nav class="navbar navbar-expand-lg">
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a>
<div class="navigation-links-guest">
<ul class="navbar-nav-guests">
<li class="nav-item dropdown">
<a class="nav-link" href="/about" data-toggle="dropdown">About</a>
<ul class="dropdown-menu">
<li><a href="/login">login</a></li>
</ul>
</li>
<li class="nav-item dropdown" id="themes">
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Themes</a>
<ul class="dropdown-menu">
@each(themes as t)
<li><a href="/theme/{{ t }}">{{ t }}</a></li>
@endeach
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link ddcontent" href="#"@if(tmp?.mime) content="{{ tmp?.mime }}" data-toggle="dropdown"@endif>Filter@if(!tmp?.mime)&nbsp;&#9660;@endif</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endif">All</a></li>
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifaudio">Audio</a></li>
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifvideo">Video</a></li>
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifimage">Image</a></li>
</ul>
</li>
<li class="nav-item">
<a id="random" class="nav-link" href="/random">
<span class="nav-link-identifier">Random</span>
</a>
</li>
</ul>
</div>
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
<div class="pagination-container-fluid">
<div class="pagination-wrapper">
@if(typeof pagination !== "undefined")
<nav class="pagination">
<a href="{{ link.main }}{{ link.path }}{{ pagination.start }}" class="page-item-1 btn start@if(!pagination.prev) disabled@endif">&laquo;</a>
<a href="{{ link.main }}{{ link.path }}{{ pagination.prev }}" class="page-item-2 btn prev@if(!pagination.prev) disabled@endif">&lsaquo;</a>
@each(pagination.cheat as i)
@if(i == pagination.page)
<span class="btn disabled">{{ i }}</span>
@else
<a href="{{ link.main }}{{ link.path }}{{ i }}" class="pagination-int-item btn">{{ i }}</a>
@endif
@endeach
<a href="{{ link.main }}{{ link.path }}{{ pagination.next }}" class="page-item-3 btn next@if(!pagination.next) disabled@endif">&rsaquo;</a>
<a href="{{ link.main }}{{ link.path }}{{ pagination.end }}" class="page-item-4 btn start@if(!pagination.next) disabled@endif">&raquo;</a>
</nav>
@endif
</div>
</div>
</div>
</nav>
@endif
+133
View File
@@ -0,0 +1,133 @@
@if(session)
<nav class="navbar navbar-expand-lg">
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a>
<div class="navigation-links">
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link" href="#" content="{{ session.user }}" data-toggle="dropdown">
<img src="@if(session.avatar)/t/{{ session.avatar }}.webp@else/s/img/ava/default.png@endif" class="avatar" /><span>{{ session.user }}</span>
</a>
<ul class="dropdown-menu">
<li><a href="/admin">adminpanel</a></li>
<li><a href="/user/{{ session.user.toLowerCase() }}/f0cks">my f0cks</a></li>
<li><a href="/user/{{ session.user.toLowerCase() }}/favs">my favs</a></li>
<li><a href="/settings">settings</a></li>
<li><a href="/search">search</a></li>
<li><a href="/about">About</a></li>
<li><a href="/ranking">Ranking</a></li>
<li><a href="/logout">logout</a></li>
</ul>
</li>
<li class="nav-item dropdown" id="themes">
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Themes</a>
<ul class="dropdown-menu">
@each(themes as t)
<li><a href="/theme/{{ t }}">{{ t }}</a></li>
@endeach
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link ddcontent" href="#"@if(tmp?.mime) content="{{ tmp?.mime }}" data-toggle="dropdown"@endif>Filter@if(!tmp?.mime)&nbsp;&#9660;@endif</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endif">All</a></li>
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifaudio">Audio</a></li>
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifvideo">Video</a></li>
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifimage">Image</a></li>
</ul>
</li>
<li class="nav-item @if(session)dropdown@endif">
<a class="nav-link ddcontent" href="#"@if(typeof session.mode !== "undefined") content="{{ modes[session.mode] ?? 'sfw' }}" data-toggle="dropdown"@endif>Mode</a>
<ul class="dropdown-menu">
@for(let i = 0; i < modes.length; i++)
<li><a class="dropdown-item" href="/mode/{{ i }}">{{ modes[i] }}</a></li>
@endfor
</ul>
</li>
<li class="nav-item">
<a id="random" class="nav-link" href="/random">
<span class="nav-link-identifier">Random</span>
</a>
</li>
</ul>
</div>
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
<div class="pagination-container-fluid">
<div class="pagination-wrapper">
@if(typeof pagination !== "undefined")
<nav class="pagination">
<a href="{{ link.main }}{{ link.path }}{{ pagination.start }}" class="page-item-1 btn start@if(!pagination.prev) disabled@endif">&laquo;</a>
<a href="{{ link.main }}{{ link.path }}{{ pagination.prev }}" class="page-item-2 btn prev@if(!pagination.prev) disabled@endif">&lsaquo;</a>
@each(pagination.cheat as i)
@if(i == pagination.page)
<span class="btn disabled">{{ i }}</span>
@else
<a href="{{ link.main }}{{ link.path }}{{ i }}" class="pagination-int-item btn">{{ i }}</a>
@endif
@endeach
<a href="{{ link.main }}{{ link.path }}{{ pagination.next }}" class="page-item-3 btn next@if(!pagination.next) disabled@endif">&rsaquo;</a>
<a href="{{ link.main }}{{ link.path }}{{ pagination.end }}" class="page-item-4 btn start@if(!pagination.next) disabled@endif">&raquo;</a>
</nav>
@endif
</div>
</div>
</div>
</nav>
@else
<nav class="navbar navbar-expand-lg">
<a class="navbar-brand" href="/"><span class="f0ck" width="" height="">F0CK</span></a>
<div class="navigation-links-guest">
<ul class="navbar-nav-guests">
<li class="nav-item dropdown">
<a class="nav-link" href="/about" data-toggle="dropdown">About</a>
<ul class="dropdown-menu">
<li><a href="/login">login</a></li>
</ul>
</li>
<li class="nav-item dropdown" id="themes">
<a class="nav-link ddcontent" href="#" content="{{ theme }}" data-toggle="dropdown">Themes</a>
<ul class="dropdown-menu">
@each(themes as t)
<li><a href="/theme/{{ t }}">{{ t }}</a></li>
@endeach
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link ddcontent" href="#"@if(tmp?.mime) content="{{ tmp?.mime }}" data-toggle="dropdown"@endif>Filter@if(!tmp?.mime)&nbsp;&#9660;@endif</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endif">All</a></li>
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifaudio">Audio</a></li>
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifvideo">Video</a></li>
<li><a class="dropdown-item" href="/@if(tmp?.user)user/{{ tmp?.user }}/@endifimage">Image</a></li>
</ul>
</li>
<li class="nav-item">
<a id="random" class="nav-link" href="/random">
<span class="nav-link-identifier">Random</span>
</a>
</li>
</ul>
</div>
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
<div class="pagination-container-fluid">
<div class="pagination-wrapper">
@if(typeof pagination !== "undefined")
<nav class="pagination">
<a href="{{ link.main }}{{ link.path }}{{ pagination.start }}" class="page-item-1 btn start@if(!pagination.prev) disabled@endif">&laquo;</a>
<a href="{{ link.main }}{{ link.path }}{{ pagination.prev }}" class="page-item-2 btn prev@if(!pagination.prev) disabled@endif">&lsaquo;</a>
@each(pagination.cheat as i)
@if(i == pagination.page)
<span class="btn disabled">{{ i }}</span>
@else
<a href="{{ link.main }}{{ link.path }}{{ i }}" class="pagination-int-item btn">{{ i }}</a>
@endif
@endeach
<a href="{{ link.main }}{{ link.path }}{{ pagination.next }}" class="page-item-3 btn next@if(!pagination.next) disabled@endif">&rsaquo;</a>
<a href="{{ link.main }}{{ link.path }}{{ pagination.end }}" class="page-item-4 btn start@if(!pagination.next) disabled@endif">&raquo;</a>
</nav>
@endif
</div>
</div>
</div>
</nav>
@endif
-37
View File
@@ -1,37 +0,0 @@
<nav class="navbar navbar-expand-lg">
<a class="navbar-brand" href="/"><span class="f0ck">F0CK</span></a>
<div class="navigation-links">
<ul class="navbar-nav">
<li class="nav-item dropdown" id="themes">
<a class="nav-link" href="#" content="{{ theme }}" data-toggle="dropdown">Theme</a>
<ul class="dropdown-menu">
@each(themes as t)
<li><a href="/theme/{{ t }}">{{ t }}</a></li>
@endeach
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/sessions">
<span class="nav-link-identifier">sessions</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/admin/log">
<span class="nav-link-identifier">Log</span>
</a>
</li>
@if(typeof totals !== "undefined")
<li class="nav-item" style="width: 100%; text-align: center">
total:&nbsp;{{ totals.total }}&nbsp;|&nbsp;tagged:&nbsp;{{ totals.tagged }}&nbsp;|&nbsp;untagged:&nbsp;{{ totals.untagged }}&nbsp;|&nbsp;sfw:&nbsp;{{ totals.sfw }}&nbsp;|&nbsp;nsfw:&nbsp;{{ totals.nsfw }}
</li>
@endif
</ul>
</div>
<div class="collapse navbar-collapse show" id="navbarSupportedContent">
<div class="pagination-container-fluid">
<div class="pagination-wrapper">
Henlo, {{ session.user }}&nbsp;&nbsp;<a href="/logout">Logout</a>
</div>
</div>
</div>
</nav>
+14
View File
@@ -0,0 +1,14 @@
@include(snippets/header)
<h3>Top10 ({{ year }}-{{ month }})</h3>
<table class="table">
@each(f0cks as f0ck)
<tr>
<td><a href="//f0ck.me/{{ f0ck.id }}"><img src="//f0ck.me/t/{{ f0ck.id }}.webp" /></a></td>
<td><a href="//f0ck.me/{{ f0ck.id }}">{{ f0ck.id }}</a></td>
<td>{{ f0ck.username }}</td>
</tr>
@endeach
</table>
@include(snippets/footer)
+49
View File
@@ -0,0 +1,49 @@
@include(snippets/header)
<div id="main">
<div class="profile_head">
@if(user.avatar)
<div class="profile_head_avatar">
<img src="/t/{{ user.avatar }}.webp" style="display: grid;width: 55px" />
</div>
@endif
<div class="layersoffear">
<div class="profile_head_username">
<span>{{ user.user }}</span>
</div>
<div class="profile_head_user_stats">
ID: {{ user.user_id }} Joined: {{ user.created_at }}
</div>
</div>
</div>
<div class="user_content_wrapper">
<div class="f0cks">
<div class="f0cks-header">
f0ck{{ count.f0cks == 1 ? '' : 's' }}: {{ count.f0cks }} <a href="{{ f0cks.link.main }}">view all</a>
</div>
@if('items' in f0cks)
<div class="posts">
@each(f0cks.items as item)
<a href="{{ f0cks.link.main }}{{ item.id }}" data-mime="{{ item.mime }}" data-mode="{{ item.tag_id ? ['','sfw','nsfw'][item.tag_id] : 'null' }}" style="background-image: url('/t/{{ item.id }}.webp')"><p></p></a>
@endeach
</div>
@else
no f0cks given
@endif
</div>
<div class="favs">
<div class="favs-header">
fav{{ count.favs == 1 ? '' : 's' }}: {{ count.favs }} <a href="{{ favs.link.main }}">view all</a>
</div>
@if('items' in favs)
<div class="posts">
@each(favs.items as item)
<a href="{{ favs.link.main }}{{ item.id }}" data-mime="{{ item.mime }}" data-mode="{{ item.tag_id ? ['','sfw','nsfw'][item.tag_id] : 'null' }}" style="background-image: url('/t/{{ item.id }}.webp')"><p></p></a>
@endeach
</div>
@else
no favorites
@endif
</div>
</div>
</div>
@include(snippets/footer)