add scout email
ci / build (push) Has been cancelled Details

This commit is contained in:
CJ_Clippy 2024-05-31 18:43:44 -08:00
parent 76e1ee60eb
commit 2faa8cfa21
22 changed files with 1087 additions and 1120 deletions

View File

@ -1,22 +1,23 @@
Dockerfile **/Dockerfile
*.dockerfile **/*.dockerfile
.dockerignore **/.dockerignore
.gitignore **/.gitignore
*~ **/*~
node_modules **/node_modules
npm-debug.log **/npm-debug.log
README.md **/README.md
.next **/.next
.git **/.git
LICENSE **/LICENSE
.nvmrc **/.nvmrc
CHECKS **/CHECKS
app.json **/app.json
.env* **/.env*
compose/ **/compose/
docker-compose.* **/docker-compose.*
.vscode **/.vscode
charts/**/charts **/charts/**/charts
**/.mocharc.json
**/.env **/.env
**/node_modules **/node_modules
@ -29,3 +30,4 @@ packages/strapi/build/
packages/strapi/node_modules/ packages/strapi/node_modules/
packages/strapi/data/ packages/strapi/data/
packages/strapi/backup packages/strapi/backup

View File

@ -22,7 +22,7 @@ metadata:
# create a tunnel to uwu-exit-node (chisel server) # create a tunnel to uwu-exit-node (chisel server)
# this allows us to have SSL in development # this allows us to have SSL in development
annotations: annotations:
chisel-operator.io/exit-node-name: "uwu-exit-node" chisel-operator.io/exit-node-name: "next-exit-node"
{{ end }} {{ end }}
spec: spec:
selector: selector:

View File

@ -7,9 +7,10 @@ spec:
selector: selector:
app.kubernetes.io/name: scout app.kubernetes.io/name: scout
ports: ports:
- name: web - name: http
port: 3000 port: 3000
targetPort: 3000 targetPort: http
protocol: TCP
--- ---
apiVersion: apps/v1 apiVersion: apps/v1
@ -33,7 +34,8 @@ spec:
- name: scout - name: scout
image: "{{ .Values.scout.containerName }}" image: "{{ .Values.scout.containerName }}"
ports: ports:
- containerPort: 5000 - name: http
containerPort: 3000
env: env:
- name: POSTGRES_REALTIME_CONNECTION_STRING - name: POSTGRES_REALTIME_CONNECTION_STRING
valueFrom: valueFrom:
@ -41,7 +43,7 @@ spec:
name: realtime name: realtime
key: postgresRealtimeConnectionString key: postgresRealtimeConnectionString
- name: STRAPI_URL - name: STRAPI_URL
value: https://strapi.futureporn.svc.cluster.local value: https://strapi.piko.sbtp.xyz
- name: SCOUT_RECENTS_TOKEN - name: SCOUT_RECENTS_TOKEN
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:

View File

@ -21,7 +21,7 @@ metadata:
# create a tunnel to uwu-exit-node (chisel server) # create a tunnel to uwu-exit-node (chisel server)
# this allows us to have SSL in development # this allows us to have SSL in development
annotations: annotations:
chisel-operator.io/exit-node-name: "uwu-exit-node" chisel-operator.io/exit-node-name: "strapi-exit-node"
{{ end }} {{ end }}
spec: spec:
selector: selector:

View File

@ -11,8 +11,7 @@ COPY . /usr/src/app
WORKDIR /usr/src/app WORKDIR /usr/src/app
RUN mkdir -p /prod/scout RUN mkdir -p /prod/scout
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN ls -la ./packages RUN pnpm deploy --filter=scout --prod /prod/scout
RUN pnpm deploy --filter=@futureporn/scout --prod /prod/scout
# COPY pnpm-lock.yaml ./ # COPY pnpm-lock.yaml ./
# RUN pnpm fetch # RUN pnpm fetch
# COPY ./packages/scout /app # COPY ./packages/scout /app
@ -21,6 +20,5 @@ RUN pnpm deploy --filter=@futureporn/scout --prod /prod/scout
FROM base AS scout FROM base AS scout
COPY --from=build /prod/scout /app COPY --from=build /prod/scout /app
WORKDIR /app WORKDIR /app
RUN ls -la
ENTRYPOINT ["pnpm"] ENTRYPOINT ["pnpm"]
CMD ["run", "start"] CMD ["run", "start"]

View File

@ -0,0 +1,4 @@
{
"extension": ["js"],
"spec": "src/**/*.spec.js"
}

View File

@ -0,0 +1,19 @@
{
"name": "common",
"type": "module",
"version": "1.0.0",
"description": "regular expressions, constants, and helper functions which are used app-wide",
"scripts": {
"test": "mocha"
},
"exports": {
"fansly": "./src/fansly.js"
},
"keywords": [],
"author": "@CJ_Clippy",
"license": "Unlicense",
"dependencies": {
"chai": "^5.1.1",
"mocha": "^10.4.0"
}
}

View File

@ -0,0 +1,621 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
chai:
specifier: ^5.1.1
version: 5.1.1
mocha:
specifier: ^10.4.0
version: 10.4.0
packages:
ansi-colors@4.1.1:
resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==}
engines: {node: '>=6'}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
assertion-error@2.0.1:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
browser-stdout@1.3.1:
resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==}
camelcase@6.3.0:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'}
chai@5.1.1:
resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==}
engines: {node: '>=12'}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
check-error@2.1.1:
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
engines: {node: '>= 16'}
chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
cliui@7.0.4:
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
debug@4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
decamelize@4.0.0:
resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==}
engines: {node: '>=10'}
deep-eql@5.0.1:
resolution: {integrity: sha512-nwQCf6ne2gez3o1MxWifqkciwt0zhl0LO1/UwVu4uMBuPmflWM4oQ70XMqHqnBJA+nhzncaqL9HVL6KkHJ28lw==}
engines: {node: '>=6'}
diff@5.0.0:
resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==}
engines: {node: '>=0.3.1'}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
escalade@3.1.2:
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
engines: {node: '>=6'}
escape-string-regexp@4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
find-up@5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
flat@5.0.2:
resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
hasBin: true
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
get-func-name@2.0.2:
resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
glob@8.1.0:
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
engines: {node: '>=12'}
deprecated: Glob versions prior to v9 are no longer supported
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
he@1.2.0:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
is-plain-obj@2.1.0:
resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
engines: {node: '>=8'}
is-unicode-supported@0.1.0:
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
engines: {node: '>=10'}
js-yaml@4.1.0:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
locate-path@6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
log-symbols@4.1.0:
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
engines: {node: '>=10'}
loupe@3.1.1:
resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==}
minimatch@5.0.1:
resolution: {integrity: sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==}
engines: {node: '>=10'}
mocha@10.4.0:
resolution: {integrity: sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==}
engines: {node: '>= 14.0.0'}
hasBin: true
ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
p-locate@5.0.0:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
pathval@2.0.0:
resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
engines: {node: '>= 14.16'}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
serialize-javascript@6.0.0:
resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
supports-color@8.1.1:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
workerpool@6.2.1:
resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==}
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
yargs-parser@20.2.4:
resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==}
engines: {node: '>=10'}
yargs-unparser@2.0.0:
resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==}
engines: {node: '>=10'}
yargs@16.2.0:
resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
engines: {node: '>=10'}
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
snapshots:
ansi-colors@4.1.1: {}
ansi-regex@5.0.1: {}
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
argparse@2.0.1: {}
assertion-error@2.0.1: {}
balanced-match@1.0.2: {}
binary-extensions@2.3.0: {}
brace-expansion@2.0.1:
dependencies:
balanced-match: 1.0.2
braces@3.0.3:
dependencies:
fill-range: 7.1.1
browser-stdout@1.3.1: {}
camelcase@6.3.0: {}
chai@5.1.1:
dependencies:
assertion-error: 2.0.1
check-error: 2.1.1
deep-eql: 5.0.1
loupe: 3.1.1
pathval: 2.0.0
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
check-error@2.1.1: {}
chokidar@3.5.3:
dependencies:
anymatch: 3.1.3
braces: 3.0.3
glob-parent: 5.1.2
is-binary-path: 2.1.0
is-glob: 4.0.3
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
fsevents: 2.3.3
cliui@7.0.4:
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
color-name@1.1.4: {}
debug@4.3.4(supports-color@8.1.1):
dependencies:
ms: 2.1.2
optionalDependencies:
supports-color: 8.1.1
decamelize@4.0.0: {}
deep-eql@5.0.1: {}
diff@5.0.0: {}
emoji-regex@8.0.0: {}
escalade@3.1.2: {}
escape-string-regexp@4.0.0: {}
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
find-up@5.0.0:
dependencies:
locate-path: 6.0.0
path-exists: 4.0.0
flat@5.0.2: {}
fs.realpath@1.0.0: {}
fsevents@2.3.3:
optional: true
get-caller-file@2.0.5: {}
get-func-name@2.0.2: {}
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
glob@8.1.0:
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 5.0.1
once: 1.4.0
has-flag@4.0.0: {}
he@1.2.0: {}
inflight@1.0.6:
dependencies:
once: 1.4.0
wrappy: 1.0.2
inherits@2.0.4: {}
is-binary-path@2.1.0:
dependencies:
binary-extensions: 2.3.0
is-extglob@2.1.1: {}
is-fullwidth-code-point@3.0.0: {}
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
is-number@7.0.0: {}
is-plain-obj@2.1.0: {}
is-unicode-supported@0.1.0: {}
js-yaml@4.1.0:
dependencies:
argparse: 2.0.1
locate-path@6.0.0:
dependencies:
p-locate: 5.0.0
log-symbols@4.1.0:
dependencies:
chalk: 4.1.2
is-unicode-supported: 0.1.0
loupe@3.1.1:
dependencies:
get-func-name: 2.0.2
minimatch@5.0.1:
dependencies:
brace-expansion: 2.0.1
mocha@10.4.0:
dependencies:
ansi-colors: 4.1.1
browser-stdout: 1.3.1
chokidar: 3.5.3
debug: 4.3.4(supports-color@8.1.1)
diff: 5.0.0
escape-string-regexp: 4.0.0
find-up: 5.0.0
glob: 8.1.0
he: 1.2.0
js-yaml: 4.1.0
log-symbols: 4.1.0
minimatch: 5.0.1
ms: 2.1.3
serialize-javascript: 6.0.0
strip-json-comments: 3.1.1
supports-color: 8.1.1
workerpool: 6.2.1
yargs: 16.2.0
yargs-parser: 20.2.4
yargs-unparser: 2.0.0
ms@2.1.2: {}
ms@2.1.3: {}
normalize-path@3.0.0: {}
once@1.4.0:
dependencies:
wrappy: 1.0.2
p-limit@3.1.0:
dependencies:
yocto-queue: 0.1.0
p-locate@5.0.0:
dependencies:
p-limit: 3.1.0
path-exists@4.0.0: {}
pathval@2.0.0: {}
picomatch@2.3.1: {}
randombytes@2.1.0:
dependencies:
safe-buffer: 5.2.1
readdirp@3.6.0:
dependencies:
picomatch: 2.3.1
require-directory@2.1.1: {}
safe-buffer@5.2.1: {}
serialize-javascript@6.0.0:
dependencies:
randombytes: 2.1.0
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
strip-ansi@6.0.1:
dependencies:
ansi-regex: 5.0.1
strip-json-comments@3.1.1: {}
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
supports-color@8.1.1:
dependencies:
has-flag: 4.0.0
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
workerpool@6.2.1: {}
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
wrappy@1.0.2: {}
y18n@5.0.8: {}
yargs-parser@20.2.4: {}
yargs-unparser@2.0.0:
dependencies:
camelcase: 6.3.0
decamelize: 4.0.0
flat: 5.0.2
is-plain-obj: 2.1.0
yargs@16.2.0:
dependencies:
cliui: 7.0.4
escalade: 3.1.2
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 20.2.4
yocto-queue@0.1.0: {}

View File

@ -0,0 +1,10 @@
const fansly = {
regex: {
username: new RegExp(/^https:\/\/fansly\.com\/(?:live\/)?([^\/]+)/)
}
}
export default fansly

View File

@ -0,0 +1,16 @@
import { expect } from 'chai'
import fansly from './fansly.js'
describe('fansly', function () {
describe('regex', function () {
describe('username', function () {
it('should get the username of the channel', function () {
expect(fansly.regex.username.exec('https://fansly.com/18Plus/posts').at(1)).to.equal('18Plus')
expect(fansly.regex.username.exec('https://fansly.com/projektmelody/posts').at(1)).to.equal('projektmelody')
expect(fansly.regex.username.exec('https://fansly.com/GoodKittenVR').at(1)).to.equal('GoodKittenVR')
expect(fansly.regex.username.exec('https://fansly.com/live/MzLewdieB').at(1)).to.equal('MzLewdieB')
expect(fansly.regex.username.exec('https://fansly.com/live/340602399334871040').at(1)).to.equal('340602399334871040')
})
})
})
})

View File

@ -0,0 +1,5 @@
import fansly from './fansly.js'
export default {
fansly
}

View File

@ -1,9 +1,4 @@
.dockerignore # STOP! This .dockerignore is probably not the .dockerignore you are looking for.
.gitignore # The dockerignore in the ROOT of the Docker context is the .dockerignore that docker uses.
*~ # We are using a monorepo and the docker build context is the root of this git repo.
node_modules # thus, see ../../.dockerignore
npm-debug.log
README.md
.git
LICENSE
.nvmrc

1
packages/scout/.nvmrc Normal file
View File

@ -0,0 +1 @@
lts/iron

View File

@ -19,3 +19,15 @@ Support for
* [ ] runs browser headless * [ ] runs browser headless
* [ ] runs in the cloud * [ ] runs in the cloud
* [ ] runs in k8s cluster * [ ] runs in k8s cluster
## Puppeteer
For when we get to the point where we need it, here are the packages we used with success during past testing.
"puppeteer": "^22.7.1",
"puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-repl": "^2.3.3",
"puppeteer-extra-plugin-stealth": "^2.11.2"

View File

@ -1,9 +1,9 @@
{ {
"name": "@futureporn/scout", "name": "scout",
"type": "module", "type": "module",
"version": "3.3.0", "version": "3.3.0",
"description": "detect when a stream goes live", "description": "detect when a stream goes live",
"main": "index.js", "main": "src/index.email.js",
"scripts": { "scripts": {
"test": "mocha", "test": "mocha",
"start": "node ./src/index.email.js", "start": "node ./src/index.email.js",
@ -13,22 +13,22 @@
"author": "@CJ_Clippy", "author": "@CJ_Clippy",
"license": "Unlicense", "license": "Unlicense",
"dependencies": { "dependencies": {
"pg-pubsub": "workspace:*",
"cheerio": "1.0.0-rc.12", "cheerio": "1.0.0-rc.12",
"common": "workspace:*",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"date-fns": "^3.6.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"fastq": "^1.17.1", "fastq": "^1.17.1",
"faye": "^1.4.0", "faye": "^1.4.0",
"imapflow": "^1.0.160", "imapflow": "^1.0.160",
"limiter": "2.0.1", "limiter": "2.0.1",
"mailparser": "^3.7.1", "mailparser": "^3.7.1",
"puppeteer": "^22.7.1", "pg-pubsub": "workspace:*",
"puppeteer-extra": "^3.3.6", "qs": "^6.12.1",
"puppeteer-extra-plugin-repl": "^2.3.3", "slugify": "^1.6.6",
"puppeteer-extra-plugin-stealth": "^2.11.2",
"xpath": "^0.0.34" "xpath": "^0.0.34"
}, },
"packageManager": "pnpm@9.1.2", "packageManager": "pnpm@9.1.3",
"devDependencies": { "devDependencies": {
"chai": "^5.1.0", "chai": "^5.1.0",
"mocha": "^10.4.0" "mocha": "^10.4.0"

File diff suppressed because it is too large Load Diff

View File

@ -49,7 +49,8 @@ export class Email extends EventEmitter {
auth: { auth: {
user: process.env.SCOUT_IMAP_USERNAME, user: process.env.SCOUT_IMAP_USERNAME,
pass: process.env.SCOUT_IMAP_PASSWORD pass: process.env.SCOUT_IMAP_PASSWORD
} },
logger: false
}); });
this.registerEventListeners() this.registerEventListeners()
@ -82,7 +83,7 @@ export class Email extends EventEmitter {
let lock = await this.client.getMailboxLock('INBOX'); let lock = await this.client.getMailboxLock('INBOX');
let dl, body let dl, body
try { try {
dl = await this.client.download(uid, undefined, { uid: true }) dl = await this.client.download(uid, null, { uid: true })
body = await streamToString(dl.content) body = await streamToString(dl.content)
} finally { } finally {
lock.release() lock.release()
@ -97,8 +98,9 @@ export class Email extends EventEmitter {
for await (let message of this.client.fetch('1:*', { envelope: true })) { for await (let message of this.client.fetch('1:*', { envelope: true })) {
// it is tempting to call this.client.download here, but that is not possible while the mailbox is locked. // it is tempting to call this.client.download here, but that is not possible while the mailbox is locked.
// client.download must be called outside of this lock // client.download must be called outside of this lock
// commenting these out because it lags Tilt
// console.log('here is a message') // console.log('here is a message')
console.log(JSON.stringify(message, null, 2)) // console.log(JSON.stringify(message, null, 2))
this.emit('message', message) this.emit('message', message)
} }
} finally { } finally {
@ -110,7 +112,7 @@ export class Email extends EventEmitter {
console.log(` > REGISTERING EVENT LISTENERS <`) console.log(` > REGISTERING EVENT LISTENERS <`)
this.client.once('end', () => this.reconnect()) this.client.once('end', () => this.reconnect())
this.client.on('exists', (evt) => { this.client.on('exists', (evt) => {
// console.log(`exists event! count=${evt.count} prevCount=${evt.prevCount}`) console.log(`exists event! count=${evt.count} prevCount=${evt.prevCount}`)
// console.log(evt) // console.log(evt)
if (evt.path === 'INBOX') { if (evt.path === 'INBOX') {
this.emitAllMessages() this.emitAllMessages()

View File

@ -25,14 +25,15 @@ async function handleMessage({email, msg}) {
await signalRealtime({ url, platform, channel, displayName, date }) await signalRealtime({ url, platform, channel, displayName, date })
console.log(' ✏️✏️ creating stream entry in db') console.log(' ✏️✏️ creating stream entry in db')
await createStreamInDb({ platform, channel, date }) await createStreamInDb({ source: 'email', platform, channel, date, url })
} }
console.log(' ✏️ archiving e-mail') console.log(' ✏️ archiving e-mail')
await email.archiveMessage(msg.uid) await email.archiveMessage(msg.uid)
} catch (e) { } catch (e) {
// console.error('error encoutered') // console.error('error encoutered')
console.error(` An error was encountered while handling the following e-mail message.\n${JSON.stringify(msg, null, 2)}\nError as follows.\n${e}`) console.error(` An error was encountered while handling the following e-mail message.\n${JSON.stringify(msg, null, 2)}\nError as follows.`)
console.error(e)
} }
} }

View File

@ -4,7 +4,7 @@ import { load } from 'cheerio'
const definitions = [ const definitions = [
{ {
platform: 'Chaturbate', platform: 'chaturbate',
selectors: { selectors: {
channel: 'td[id*="onlinemessage"] a:nth-child(1)' channel: 'td[id*="onlinemessage"] a:nth-child(1)'
}, },
@ -12,7 +12,7 @@ const definitions = [
template: 'https://chaturbate.com/:channel' template: 'https://chaturbate.com/:channel'
}, },
{ {
platform: 'Fansly', platform: 'fansly',
selectors: { selectors: {
url: ($) => $("a[href*='/live/']").attr('href'), url: ($) => $("a[href*='/live/']").attr('href'),
displayName: 'div[class*="message-col"] div:nth-child(5)' displayName: 'div[class*="message-col"] div:nth-child(5)'
@ -38,10 +38,16 @@ function render(template, values) {
* *
* Check an e-mail for go-live notification content. * Check an e-mail for go-live notification content.
* *
* { isMatch, url, platform, channel, displayName, date }
*
* @param {String} body -- raw mail body * @param {String} body -- raw mail body
* @returns {Object} result * @returns {Object} result
* @returns {Boolean} result.isMatch true if e-mail contains a go-live notification * @returns {Boolean} result.isMatch true if e-mail contains a go-live notification
* @returns {String} result.channel * @returns {String} result.url example: https://fansly.com/projektmelody
* @returns {String} result.platform example: fansly
* @returns {String} result.channel example: projektmelody
* @returns {String} result.displayName example: ProjektMelody
* @returns {String} result.date example: 2024-05-31T01:02:00.000Z
*/ */
export async function checkEmail (body) { export async function checkEmail (body) {

View File

@ -12,7 +12,7 @@ describe('parsers', function () {
const mailBody = await fs.readFile(path.join(__dirname, './fixtures/fansly.fixture.txt'), { encoding: 'utf8' }) const mailBody = await fs.readFile(path.join(__dirname, './fixtures/fansly.fixture.txt'), { encoding: 'utf8' })
const { isMatch, channel, platform, url, date } = await checkEmail(mailBody) const { isMatch, channel, platform, url, date } = await checkEmail(mailBody)
expect(isMatch).to.equal(true, 'a Fansly heuristic was not found') expect(isMatch).to.equal(true, 'a Fansly heuristic was not found')
expect(platform).to.equal('Fansly') expect(platform).to.equal('fansly')
expect(channel).to.equal('SkiaObsidian') expect(channel).to.equal('SkiaObsidian')
expect(url).to.equal('https://fansly.com/live/SkiaObsidian') expect(url).to.equal('https://fansly.com/live/SkiaObsidian')
expect(date).to.equal('2024-05-05T03:04:33.000Z') expect(date).to.equal('2024-05-05T03:04:33.000Z')
@ -21,7 +21,7 @@ describe('parsers', function () {
const mailBody = await fs.readFile(path.join(__dirname, './fixtures/chaturbate.fixture.txt'), { encoding: 'utf8' }) const mailBody = await fs.readFile(path.join(__dirname, './fixtures/chaturbate.fixture.txt'), { encoding: 'utf8' })
const { isMatch, channel, platform, url, date } = await checkEmail(mailBody) const { isMatch, channel, platform, url, date } = await checkEmail(mailBody)
expect(isMatch).to.equal(true, 'a CB heuristic was not found') expect(isMatch).to.equal(true, 'a CB heuristic was not found')
expect(platform).to.equal('Chaturbate') expect(platform).to.equal('chaturbate')
expect(channel).to.equal('skyeanette') expect(channel).to.equal('skyeanette')
expect(url).to.equal('https://chaturbate.com/skyeanette') expect(url).to.equal('https://chaturbate.com/skyeanette')
expect(date).to.equal('2023-07-24T01:08:28.000Z') expect(date).to.equal('2023-07-24T01:08:28.000Z')

View File

@ -2,6 +2,9 @@
import 'dotenv/config' import 'dotenv/config'
// import { PgPubSub } from '@imqueue/pg-pubsub'; // @see https://github.com/imqueue/pg-pubsub/issues/20 // import { PgPubSub } from '@imqueue/pg-pubsub'; // @see https://github.com/imqueue/pg-pubsub/issues/20
import { PgPubSub } from 'pg-pubsub' import { PgPubSub } from 'pg-pubsub'
import qs from 'qs'
import { subMinutes, addMinutes } from 'date-fns'
import slugify from 'slugify'
// alternative js libraries for postgres notify/wait // alternative js libraries for postgres notify/wait
// * https://github.com/imqueue/pg-pubsub // * https://github.com/imqueue/pg-pubsub
@ -36,27 +39,214 @@ export async function signalRealtime ({ url, platform, channel, displayName, dat
/** /**
* Create a database record which shows this stream exists * Create a database record which shows this stream exists
*
* It's kind of complicated, but we do it this way so we don't need a backend batch processor.
* Instead, Scout takes as much responsibility as possible and does the work that a human would do if they were creating the records.
*
* In Strapi, we are finding or updating or creating the following content-types.
* * vtuber
* * platform-notification
* * stream
*
* It's a 3 step process, with each step outlined in the function body.
*/ */
export async function createStreamInDb ({ platform, channel, date }) { export async function createStreamInDb ({ source, platform, channel, date, url }) {
const url = `${process.env.STRAPI_URL}/api/streams`
console.log(`we are going to fetch POST ${url} now.`) let vtuberId, streamId
const res = await fetch(url, {
console.log('>># Step 1')
// # Step 1.
// First we find or create the vtuber
// The vtuber may already be in the db, so we look for that record. All we need is the Vtuber ID.
// If the vtuber is not in the db, we create the vtuber record.
// GET /api/:pluralApiId?filters[field][operator]=value
const findVtubersQueryString = qs.stringify({
filters: {
chaturbate: (platform === 'chaturbate') ? { '$eq': url } : null,
fansly: (platform === 'fansly') ? { '$eq': url } : null
}
})
console.log('>> findVtuber')
const findVtuberRes = await fetch(`${process.env.STRAPI_URL}/api/vtubers?${findVtubersQueryString}`, {
method: 'GET',
headers: {
'content-type': 'application/json'
}
})
const findVtuberJson = await findVtuberRes.json()
if (findVtuberJson.data.length > 0) {
console.log('>>a vtuber was FOUND')
vtuberId = findVtuberJson.data.id
console.log('here is the findVtuberJson (as follows)')
console.log(findVtuberJson)
console.log(`the matching vtuber has ID=${vtuberId} (${findVtuberJson.data.attributes.displayName})`)
}
if (!vtuberId) {
console.log('>> vtuberId was not found so we create')
const createVtuberRes = await fetch(`${process.env.STRAPI_URL}/api/vtubers`, {
method: 'POST', method: 'POST',
headers: { headers: {
'authorization': `Bearer ${process.env.SCOUT_STRAPI_API_KEY}` 'authorization': `Bearer ${process.env.SCOUT_STRAPI_API_KEY}`,
'content-type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
data: {
'displayName': channel,
'fansly': (platform === 'fansly') ? url : null,
'chaturbate': (platform === 'chaturbate') ? url : null,
'slug': slugify(channel),
'description1': ' ',
'image': 'https://placehold.co/200x200.png',
'themeColor': '#dde1ec'
}
})
})
const createVtuberJson = await createVtuberRes.json()
console.log('>> createVtuberJson as follows')
console.log(createVtuberJson)
if (createVtuberJson.data) {
vtuberId = createVtuberJson.data.id
console.log(`>>> vtuber created with id=${vtuberId}`)
}
}
if (!vtuberId) throw new Error(`>> we weren't able to find or create a vtuberId so we are panicking. (this should not happen under normal circumstances. Bug desu ka?)`)
console.log(`>># Step 2. vtuberId=${vtuberId}`)
// # Step 2.
// Next we create the platform-notification record.
// This probably doesn't already exist, so we don't check for a pre-existing platform-notification.
const pNotifPayload = {
data: {
source: source,
date: date,
date2: date,
platform: platform,
vtuber: vtuberId,
}
}
console.log('pNotifPayload as follows')
console.log(pNotifPayload)
const pNotifCreateRes = await fetch(`${process.env.STRAPI_URL}/api/platform-notifications`, {
method: 'POST',
headers: {
'authorization': `Bearer ${process.env.SCOUT_STRAPI_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(pNotifPayload)
})
const pNotifData = await pNotifCreateRes.json()
if (pNotifData.error) {
console.error('>> we failed to create platform-notification, there was an error in the response')
console.error(pNotifData.error)
throw new Error(pNotifData.error)
}
console.log(`>> pNotifData (json response) is as follows`)
console.log(pNotifData)
if (!pNotifData.data?.id) throw new Error('failed to created pNotifData! The response was missing an id');
// # Step 3.
// Finally we find or create the stream record
// The stream may already be in the db (the streamer is multi-platform streaming), so we look for that record.
// This gets a bit tricky. How do we determine one stream from another?
// For now, the rule is 30 minutes of separation.
// Anything <=30m is interpreted as the same stream. Anything >30m is interpreted as a different stream.
// If the stream is not in the db, we create the stream record
// qs.stringify({
// populate: 'vtuber',
// filters: {
// date: {
// "$eq": '2024-01-09T08:00:00.000Z'
// },
// vtuber: {
// id: {
// '$eq': 1
// }
// }
// }
// }, {
// encode: false
// })
const dateSinceRange = subMinutes(new Date(date), 30)
const dateUntilRange = addMinutes(new Date(date), 30)
console.log(`Find a stream within + or - 30 mins of the notif date=${new Date(date).toISOString()}. dateSinceRange=${dateSinceRange.toISOString()}, dateUntilRange=${dateUntilRange.toISOString()}`)
const findStreamQueryString = qs.stringify({
populate: '*',
filters: {
date: {
$gte: dateSinceRange,
$lte: dateUntilRange
},
vtuber: {
id: {
'$eq': vtuberId
}
}
}
}, { encode: false })
console.log('>> findStream')
const findStreamRes = await fetch(`${process.env.STRAPI_URL}/api/streams?${findStreamQueryString}`, {
method: 'GET',
headers: {
'authorization': `Bearer ${process.env.SCOUT_STRAPI_API_KEY}`,
'Content-Type': 'application/json'
}
})
const findStreamData = await findStreamRes.json()
if (findStreamData.data) {
console.log('>> we found a findStreamData json')
console.log(findStreamData)
streamId = findStreamData.data?.id
}
if (!streamId) {
console.log('>> did not find a streamId')
const createStreamPayload = {
data: { data: {
isFanslyStream: (platform === 'Fansly') ? true : false, isFanslyStream: (platform === 'Fansly') ? true : false,
isChaturbateStream: (platform === 'Chaturbate') ? true : false, isChaturbateStream: (platform === 'Chaturbate') ? true : false,
archiveStatus: 'missing', archiveStatus: 'missing',
date: date date: date,
date2: date,
date_str: date,
vtuber: vtuberId,
platformNotifications: [
pNotifData.data.id
]
} }
}
console.log('>>createStreamPayload as follows')
console.log(createStreamPayload)
const createStreamRes = await fetch(`${process.env.STRAPI_URL}/api/streams`, {
method: 'POST',
headers: {
'authorization': `Bearer ${process.env.SCOUT_STRAPI_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify(createStreamPayload)
}) })
}) const createStreamJson = await createStreamRes.json()
const json = await res.json() console.log('>>we got the createStreamJson')
console.log('we got the json') console.log(createStreamJson)
console.log(json) if (createStreamJson.error) {
console.error(JSON.stringify(createStreamJson.error, null, 2))
throw new Error('Failed to create stream in DB due to an error. (see above)')
}
}
} }

View File

@ -124,7 +124,7 @@ docker_build(
docker_build( docker_build(
'fp/scout', 'fp/scout',
'.', '.',
only=['./pnpm-lock.yaml', './package.json', './packages/scout', './packages/pg-pubsub'], only=['./pnpm-lock.yaml', './package.json', './packages/scout', './packages/pg-pubsub', './packages/common'],
dockerfile='d.scout.dockerfile', dockerfile='d.scout.dockerfile',
target='scout', target='scout',
live_update=[ live_update=[
@ -181,13 +181,7 @@ k8s_resource(
# workload='strapi-app', # workload='strapi-app',
# port_forwards=['1338'] # port_forwards=['1338']
# ) # )
k8s_resource(
workload='postgres',
)
k8s_resource(
workload='scout',
port_forwards=['5000']
)
k8s_resource( k8s_resource(
workload='postgres', workload='postgres',
port_forwards=['5432'] port_forwards=['5432']