Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
fc83176522
|
|||
|
0506aaaac8
|
|||
|
bd9724da69
|
|||
|
3bad433f1a
|
|||
|
2bd7a5176f
|
|||
|
a9ff01261b
|
|||
|
1362ebc3c2
|
|||
|
2de6131ba7
|
|||
|
1762962f32
|
|||
|
7fb9e46c05
|
|||
|
b33488342a
|
|||
|
d53b14b1de
|
|||
|
a6a6d933da
|
|||
| 0803f3efff | |||
| ec0420962b | |||
| b407202e53 | |||
| b9b8474455 | |||
| 76853637bc | |||
| dac3777fc4 | |||
| bb5a3697f2 | |||
| e73aacb900 | |||
| ea483da645 | |||
| 85d35f86a7 | |||
| 3d03ab1ea4 | |||
| 4ca3ccdad3 | |||
| 10e26cda72 | |||
| c648dfd147 | |||
| 2f97821dc6 | |||
| 3d78b90f3a | |||
| 3dc8228e22 | |||
| 93171ad8e6 | |||
| dca7ad0902 |
@@ -0,0 +1,24 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[*.{json,jsonc}]
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
|
||||
[{Makefile,makefile}]
|
||||
indent_style = tab
|
||||
@@ -36,4 +36,4 @@ jobs:
|
||||
else
|
||||
echo ">>> Skipping $dir: no compose file found"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
@@ -104,4 +104,6 @@ temp/*
|
||||
*/k8s/*secret*
|
||||
!*/k8s/*secret*.example
|
||||
traefik/k8s/local-tls.yaml
|
||||
converters/k8s/config.yaml
|
||||
convertx/k8s/config.yaml
|
||||
traefik/k8s/crowdsec-middleware.yaml
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
ignored:
|
||||
- DL3008
|
||||
- DL3042
|
||||
- DL3018
|
||||
- DL3059
|
||||
trustedRegistries:
|
||||
- docker.io
|
||||
- ghcr.io
|
||||
- quay.io
|
||||
- gcr.forust.xyz
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"default": true,
|
||||
"MD013": false,
|
||||
"MD024": false,
|
||||
"MD033": false,
|
||||
"MD041": false,
|
||||
"MD046": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
bracketSameLine: true
|
||||
htmlWhitespaceSensitivity: css
|
||||
printWidth: 120
|
||||
tabWidth: 2
|
||||
trailingComma: all
|
||||
proseWrap: preserve
|
||||
endOfLine: lf
|
||||
@@ -0,0 +1,22 @@
|
||||
extends: default
|
||||
|
||||
rules:
|
||||
comments:
|
||||
min-spaces-from-content: 1
|
||||
comments-indentation: false
|
||||
document-start: disable
|
||||
line-length: disable
|
||||
braces:
|
||||
min-spaces-inside: 0
|
||||
max-spaces-inside: 1
|
||||
brackets:
|
||||
min-spaces-inside: 0
|
||||
max-spaces-inside: 1
|
||||
indentation:
|
||||
spaces: 2
|
||||
indent-sequences: consistent
|
||||
truthy:
|
||||
allowed-values:
|
||||
- "true"
|
||||
- "false"
|
||||
- "on"
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"tab_size": 2,
|
||||
"soft_wrap": "prefer_line",
|
||||
"preferred_line_length": 120,
|
||||
"format_on_save": "on",
|
||||
"languages": {
|
||||
"YAML": {
|
||||
"tab_size": 2,
|
||||
"hard_tabs": false,
|
||||
"format_on_save": "on",
|
||||
"formatter": {
|
||||
"language_server": { "name": "yaml-language-server" }
|
||||
}
|
||||
},
|
||||
"Python": {
|
||||
"tab_size": 4,
|
||||
"format_on_save": "on",
|
||||
"formatter": {
|
||||
"language_server": { "name": "ruff" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"lsp": {
|
||||
"yaml-language-server": {
|
||||
"settings": {
|
||||
"yaml": {
|
||||
"schemas": {
|
||||
"kubernetes": ["**/k8s/*.yaml", "**/k8s/*.yml"]
|
||||
},
|
||||
"validate": true,
|
||||
"completion": true,
|
||||
"format": {
|
||||
"enable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,11 @@ services:
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.adguard.loadbalancer.server.port=3000"
|
||||
|
||||
|
||||
# Prod Router
|
||||
- "traefik.http.routers.adguard.rule=Host(`dns.forust.xyz`) || Host(`adguard.forust.xyz`)"
|
||||
- "traefik.http.routers.adguard.entrypoints=websecure"
|
||||
- "traefik.http.routers.adguard.tls=true"
|
||||
- "traefik.http.routers.adguard.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.adguard-local.rule=Host(`adguard.workstation.internal`) || Host(`dns.workstation.internal`)"
|
||||
- "traefik.http.routers.adguard-local.entrypoints=websecure"
|
||||
@@ -31,10 +31,10 @@ services:
|
||||
- "traefik.http.routers.adguard-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.adguard-dev.tls=true"
|
||||
# DoH Router
|
||||
- "traefik.http.routers.dns.rule=(Host(`dns.forust.xyz`) && PathPrefix(`/dns-query`))"
|
||||
- "traefik.http.routers.dns.entrypoints=websecure"
|
||||
- "traefik.http.routers.dns.tls.certresolver=letsencrypt"
|
||||
|
||||
- "traefik.http.routers.dns-over-https.rule=(Host(`dns.forust.xyz` || Host(`adguard.forust.xyz`)) && PathPrefix(`/dns-query`))"
|
||||
- "traefik.http.routers.dns-over-https.entrypoints=websecure"
|
||||
- "traefik.http.routers.dns-over-https.tls.certresolver=letsencrypt"
|
||||
|
||||
# Glance Metadata
|
||||
- glance.name=adguard
|
||||
- glance.url=https://adguard.forust.xyz/
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: adguard-lb-service
|
||||
namespace: adguard
|
||||
annotations:
|
||||
metallb.io/loadBalancerIPs: "192.168.80.3"
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
externalTrafficPolicy: Local
|
||||
selector:
|
||||
app: adguard
|
||||
ports:
|
||||
- name: dns-udp
|
||||
port: 53
|
||||
targetPort: 53
|
||||
protocol: UDP
|
||||
- name: dns-tcp
|
||||
port: 53
|
||||
targetPort: 53
|
||||
protocol: TCP
|
||||
- name: dot
|
||||
port: 853
|
||||
targetPort: 853
|
||||
protocol: TCP
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: adguard-service
|
||||
namespace: adguard
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^(adguard|dns)\.forust\.xyz$`)
|
||||
- match: Host(`adguard.forust.xyz`) || Host(`dns.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
services:
|
||||
- name: adguard-service
|
||||
port: 3000
|
||||
@@ -24,7 +26,7 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^(adguard|dns)\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`adguard.workstation.internal`) || Host(`dns.workstation.internal`) || Host(`adguard.gigaforust.internal`) || Host(`dns.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: adguard-service
|
||||
@@ -39,24 +41,12 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^(adguard|dns)\.forust\.xyz$`) && PathPrefix(`/dns-query`)
|
||||
- match: (Host(`adguard.forust.xyz`) || Host(`dns.forust.xyz`)) && PathPrefix(`/dns-query`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
services:
|
||||
- name: adguard-service
|
||||
port: 3000
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRouteTCP
|
||||
metadata:
|
||||
name: adguard-dot
|
||||
namespace: adguard
|
||||
spec:
|
||||
entryPoints:
|
||||
- dot
|
||||
routes:
|
||||
- match: HostSNI(`*`)
|
||||
services:
|
||||
- name: adguard-service
|
||||
port: 853
|
||||
|
||||
@@ -47,7 +47,7 @@ services:
|
||||
# Prod Router
|
||||
- "traefik.http.routers.authentik-server.rule=Host(`auth.forust.xyz`)"
|
||||
- "traefik.http.routers.authentik-server.entrypoints=websecure"
|
||||
- "traefik.http.routers.authentik-server.tls=true"
|
||||
- "traefik.http.routers.authentik-server.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.authentik-server-local.rule=Host(`auth.workstation.internal`)"
|
||||
- "traefik.http.routers.authentik-server-local.entrypoints=websecure"
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^auth\.forust\.xyz$`)
|
||||
- match: Host(`auth.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
services:
|
||||
- name: authentik-server-service
|
||||
port: 9000
|
||||
@@ -24,9 +26,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^auth\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`auth.workstation.internal`) || Host(`auth.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: authentik-server-service
|
||||
port: 9000
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ services:
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
network_mode: 'host'
|
||||
network_mode: "host"
|
||||
# https://github.com/timothymiller/cloudflare-ddns#-quick-start
|
||||
environment:
|
||||
- CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN:?Cloudflare API token is required}
|
||||
|
||||
@@ -21,7 +21,7 @@ services:
|
||||
# Prod Router
|
||||
- "traefik.http.routers.checkmk.rule=Host(`cmk.forust.xyz`)"
|
||||
- "traefik.http.routers.checkmk.entrypoints=websecure"
|
||||
- "traefik.http.routers.checkmk.tls=true"
|
||||
- "traefik.http.routers.checkmk.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.checkmk-local.rule=Host(`cmk.workstation.internal`)"
|
||||
- "traefik.http.routers.checkmk-local.entrypoints=websecure"
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^cmk\.forust\.xyz$`)
|
||||
- match: Host(`cmk.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-crowdsec-bouncer@kubernetescrd
|
||||
services:
|
||||
- name: checkmk-service
|
||||
port: 5000
|
||||
@@ -24,9 +26,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^cmk\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`cmk.workstation.internal`) || Host(`cmk.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: checkmk-service
|
||||
port: 5000
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
ACCOUNT_REGISTRATION=false
|
||||
HTTP_ALLOWED=false
|
||||
ALLOW_UNAUTHENTICAED=false
|
||||
AUTO_DELETE_EVERY_N_HOURS=24
|
||||
WEBROOT=/convert
|
||||
HIDE_HISTORY=false
|
||||
LANGUAGE=en
|
||||
UNAUTHED_USER_SHARING=false
|
||||
MAX_CONVERT_PROCESS=0
|
||||
@@ -0,0 +1,70 @@
|
||||
services:
|
||||
convertx:
|
||||
container_name: convertx
|
||||
image: ghcr.io/c4illin/convertx:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9992:3000"
|
||||
# https://github.com/C4illin/ConvertX#environment-variables
|
||||
environment:
|
||||
- JWT_SECRET=$(JWT_SECRET)
|
||||
- ACCOUNT_REGISTRATION=$(ACCOUNT_REGISTRATION:-false)
|
||||
- HTTP_ALLOWED=$(HTTP_ALLOWED:-false)
|
||||
- ALLOW_UNAUTHENTICATED=$(ALLOW_UNAUTHENTICATED:-false)
|
||||
- AUTO_DELETE_EVERY_N_HOURS=$(AUTO_DELETE_EVERY_N_HOURS:-24)
|
||||
- WEBROOT=$(WEBROOT)
|
||||
- HIDE_HISTORY=$(HIDE_HISTORY:-false)
|
||||
- LANGUAGE=$(LANGUAGE:-en)
|
||||
- UNAUTHENTICATED_USER_SHARING=$(UNAUTHENTICATED_USER_SHARING:-false)
|
||||
- MAX_CONVERT_PROCESS=$(MAX_CONVERT_PROCESS:-0)
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.convertx.loadbalancer.server.port=3000"
|
||||
# Prod Router
|
||||
- "traefik.http.routers.convertx.rule=(Host(`forust.xyz`) || Host(`www.forust.xyz`)) && PathPrefix(`/convert`)"
|
||||
- "traefik.http.routers.convertx.entrypoints=websecure"
|
||||
- "traefik.http.routers.convertx.priority=50"
|
||||
- "traefik.http.routers.convertx.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.convertx-local.rule=Host(`workstation.internal`) && PathPrefix(`/convert`)"
|
||||
- "traefik.http.routers.convertx-local.entrypoints=websecure"
|
||||
- "traefik.http.routers.convertx-local.priority=50"
|
||||
- "traefik.http.routers.convertx-local.tls=true"
|
||||
# Dev Router
|
||||
- "traefik.http.routers.convertx-dev.rule=Host(`gigaforust.internal`) && PathPrefix(`/convert`)"
|
||||
- "traefik.http.routers.convertx-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.convertx-dev.priority=50"
|
||||
- "traefik.http.routers.convertx-dev.tls=true"
|
||||
networks:
|
||||
- proxy
|
||||
volumes:
|
||||
- data:/app/data
|
||||
|
||||
bentopdf:
|
||||
container_name: bentopdf
|
||||
image: bentopdf/bentopdf:latest
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.bentopdf.loadbalancer.server.port=8080"
|
||||
|
||||
# Prod router
|
||||
- "traefik.http.routers.bentopdf.rule=Host(`pdf.forust.xyz`)"
|
||||
- "traefik.http.routers.bentopdf.entrypoints=websecure"
|
||||
- "traefik.http.routers.bentopdf.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.bentopdf.tls=true"
|
||||
# Local router
|
||||
- "traefik.http.routers.bentopdf-local.rule=Host(`pdf.wokstation.internal`)"
|
||||
- "traefik.http.routers.bentopdf-local.entrypoints=websecure"
|
||||
- "traefik.http.routers.bentopdf-local.tls=true"
|
||||
# Dev router
|
||||
- "traefik.http.routers.bentopdf-dev.rule=Host(`pdf.gigaforust.internal`)"
|
||||
- "traefik.http.routers.bentopdf-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.bentopdf-dev.tls=true"
|
||||
networks:
|
||||
- proxy
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
volumes:
|
||||
data:
|
||||
@@ -0,0 +1,42 @@
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: bentopdf-service
|
||||
namespace: converters
|
||||
spec:
|
||||
selector:
|
||||
app: bentopdf
|
||||
ports:
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: bentopdf-deployment
|
||||
namespace: converters
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: bentopdf
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: bentopdf
|
||||
spec:
|
||||
containers:
|
||||
- image: bentopdf/bentopdf:latest
|
||||
imagePullPolicy: Always
|
||||
name: bentopdf
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
resources:
|
||||
requests:
|
||||
memory: "50Mi"
|
||||
cpu: "50m"
|
||||
ephemeral-storage: "100Mi"
|
||||
limits:
|
||||
memory: "700Mi"
|
||||
cpu: "700m"
|
||||
ephemeral-storage: "5Gi"
|
||||
@@ -3,7 +3,7 @@ apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: convertx-config
|
||||
namespace: convertx
|
||||
namespace: converters
|
||||
data:
|
||||
ACCOUNT_REGISTRATION: "false"
|
||||
HTTP_ALLOWED: "false"
|
||||
@@ -2,7 +2,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: convertx-service
|
||||
namespace: convertx
|
||||
namespace: converters
|
||||
spec:
|
||||
selector:
|
||||
app: convertx
|
||||
@@ -14,7 +14,7 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: convertx-deployment
|
||||
namespace: convertx
|
||||
namespace: converters
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
@@ -54,7 +54,7 @@ apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: convertx-pvc
|
||||
namespace: convertx
|
||||
namespace: converters
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
@@ -0,0 +1,65 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: convertx-prod
|
||||
namespace: converters
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: (Host(`forust.xyz`) || Host(`www.forust.xyz`)) && PathPrefix(`/convert`)
|
||||
kind: Rule
|
||||
priority: 50
|
||||
services:
|
||||
- name: convertx-service
|
||||
port: 3000
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: convertx-local
|
||||
namespace: converters
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: (Host(`workstation.internal`) || Host(`gigaforust.internal`)) && PathPrefix(`/convert`)
|
||||
kind: Rule
|
||||
priority: 50
|
||||
services:
|
||||
- name: convertx-service
|
||||
port: 3000
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: bentopdf-prod
|
||||
namespace: converters
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`pdf.forust.xyz`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: bentopdf-service
|
||||
port: 8080
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: bentopdf-local
|
||||
namespace: converters
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`pdf.workstation.internal`) || Host(`pdf.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: bentopdf-service
|
||||
port: 8080
|
||||
@@ -1,4 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: convertx
|
||||
name: converters
|
||||
@@ -2,6 +2,7 @@ apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: convertx-secrets
|
||||
namespace: converters
|
||||
type: Opaque
|
||||
stringData:
|
||||
jwt-secret: ""
|
||||
@@ -1,43 +0,0 @@
|
||||
services:
|
||||
convertx:
|
||||
container_name: convertx
|
||||
image: ghcr.io/c4illin/convertx:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9992:3000"
|
||||
# https://github.com/C4illin/ConvertX#environment-variables
|
||||
environment:
|
||||
- JWT_SECRET=$(JWT_SECRET)
|
||||
- ACCOUNT_REGISTRATION=$(ACCOUNT_REGISTRATION:-false)
|
||||
- HTTP_ALLOWED=$(HTTP_ALLOWED:-false)
|
||||
- ALLOW_UNAUTHENTICATED=$(ALLOW_UNAUTHENTICATED:-false)
|
||||
- AUTO_DELETE_EVERY_N_HOURS=$(AUTO_DELETE_EVERY_N_HOURS:-24)
|
||||
- WEBROOT=$(WEBROOT)
|
||||
- HIDE_HISTORY=$(HIDE_HISTORY:-false)
|
||||
- LANGUAGE=$(LANGUAGE:-en)
|
||||
- UNAUTHENTICATED_USER_SHARING=$(UNAUTHENTICATED_USER_SHARING:-false)
|
||||
- MAX_CONVERT_PROCESS=$(MAX_CONVERT_PROCESS:-0)
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.convertx.loadbalancer.server.port=3000"
|
||||
# Prod Router
|
||||
- "traefik.http.routers.convertx.rule=Host(`convert.forust.xyz`)"
|
||||
- "traefik.http.routers.convertx.entrypoints=websecure"
|
||||
- "traefik.http.routers.convertx.tls=true"
|
||||
# Local Router
|
||||
- "traefik.http.routers.convertx-local.rule=Host(`convert.workstation.internal`)"
|
||||
- "traefik.http.routers.convertx-local.entrypoints=websecure"
|
||||
- "traefik.http.routers.convertx-local.tls=true"
|
||||
# Dev Router
|
||||
- "traefik.http.routers.convertx-dev.rule=Host(`convertx.gigaforust.internal`)"
|
||||
- "traefik.http.routers.convertx-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.convertx-dev.tls=true"
|
||||
networks:
|
||||
- proxy
|
||||
volumes:
|
||||
- data:/app/data
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
volumes:
|
||||
data:
|
||||
@@ -1,32 +0,0 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: convertx-prod
|
||||
namespace: convertx
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^forust\.xyz$`) && PathPrefix(`/convert`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: convertx-service
|
||||
port: 3000
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: convertx-local
|
||||
namespace: convertx
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^(workstation|gigaforust)\.internal$`) && PathPrefix(`/convert`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: convertx-service
|
||||
port: 3000
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: crowdsec-bouncer
|
||||
namespace: crowdsec
|
||||
spec:
|
||||
plugin:
|
||||
crowdsec-bouncer:
|
||||
enabled: true
|
||||
LogLevel: INFO
|
||||
CrowdsecMode: live
|
||||
CrowdsecLapiScheme: http
|
||||
CrowdsecLapiHost: crowdsec-service.crowdsec.svc.cluster.local:8080
|
||||
CrowdsecLapiKeyFile: "/etc/traefik/secrets/traefik-api-key"
|
||||
@@ -0,0 +1,57 @@
|
||||
container_runtime: containerd
|
||||
agent:
|
||||
env:
|
||||
- name: COLLECTIONS
|
||||
value: "crowdsecurity/traefik crowdsecurity/base-http-scenarios crowdsecurity/sshd"
|
||||
extraVolumes:
|
||||
- name: journal-dir
|
||||
hostPath:
|
||||
path: /var/log/journal
|
||||
type: DirectoryOrCreate
|
||||
- name: run-journal-dir
|
||||
hostPath:
|
||||
path: /run/log/journal
|
||||
type: DirectoryOrCreate
|
||||
extraVolumeMounts:
|
||||
- name: journal-dir
|
||||
mountPath: /var/log/journal
|
||||
readOnly: true
|
||||
- name: run-journal-dir
|
||||
mountPath: /run/log/journal
|
||||
readOnly: true
|
||||
|
||||
acquisition:
|
||||
- namespace: traefik
|
||||
podName: "*traefik*"
|
||||
program: traefik
|
||||
poll_without_inotify: true
|
||||
|
||||
acquisitionCustom: |
|
||||
- source: journalctl
|
||||
journalctl_filter:
|
||||
- _SYSTEMD_UNIT=sshd.service
|
||||
labels:
|
||||
type: syslog
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 100Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 500Mi
|
||||
|
||||
lapi:
|
||||
env:
|
||||
- name: COLLECTIONS
|
||||
value: "crowdsecurity/traefik crowdsecurity/base-http-scenarios crowdsecurity/sshd"
|
||||
service:
|
||||
type: NodePort
|
||||
nodePort: 30011
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 150Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 500Mi
|
||||
+41
-41
@@ -1,46 +1,46 @@
|
||||
services:
|
||||
dockmon:
|
||||
image: darthnorse/dockmon:latest
|
||||
container_name: dockmon
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
# - 8000:443
|
||||
volumes:
|
||||
- data:/app/data
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "-k", "-f", "https://localhost:443/health" ]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.dockmon.loadbalancer.server.port=443"
|
||||
- "traefik.http.services.dockmon.loadbalancer.server.scheme=https"
|
||||
- "traefik.http.services.dockmon.loadbalancer.serverstransport=insecureTransport@file"
|
||||
dockmon:
|
||||
image: darthnorse/dockmon:latest
|
||||
container_name: dockmon
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
# - 8000:443
|
||||
volumes:
|
||||
- data:/app/data
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-k", "-f", "https://localhost:443/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.dockmon.loadbalancer.server.port=443"
|
||||
- "traefik.http.services.dockmon.loadbalancer.server.scheme=https"
|
||||
- "traefik.http.services.dockmon.loadbalancer.serverstransport=insecureTransport@file"
|
||||
|
||||
# Prod Router
|
||||
- "traefik.http.routers.dockmon.rule=Host(`dockmon.forust.xyz`)"
|
||||
- "traefik.http.routers.dockmon.entrypoints=websecure"
|
||||
- "traefik.http.routers.dockmon.middlewares=security-headers@file"
|
||||
- "traefik.http.routers.dockmon.tls=true"
|
||||
# Local Router
|
||||
- "traefik.http.routers.dockmon-local.rule=Host(`dockmon.workstation.internal`)"
|
||||
- "traefik.http.routers.dockmon-local.entrypoints=websecure"
|
||||
- "traefik.http.routers.dockmon-local.tls=true"
|
||||
# Dev Router
|
||||
- "traefik.http.routers.dockmon-dev.rule=Host(`dockmon.gigaforust.internal`)"
|
||||
- "traefik.http.routers.dockmon-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.dockmon-dev.tls=true"
|
||||
# Prod Router
|
||||
- "traefik.http.routers.dockmon.rule=Host(`dockmon.forust.xyz`)"
|
||||
- "traefik.http.routers.dockmon.entrypoints=websecure"
|
||||
- "traefik.http.routers.dockmon.middlewares=security-headers@file"
|
||||
- "traefik.http.routers.dockmon.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.dockmon-local.rule=Host(`dockmon.workstation.internal`)"
|
||||
- "traefik.http.routers.dockmon-local.entrypoints=websecure"
|
||||
- "traefik.http.routers.dockmon-local.tls=true"
|
||||
# Dev Router
|
||||
- "traefik.http.routers.dockmon-dev.rule=Host(`dockmon.gigaforust.internal`)"
|
||||
- "traefik.http.routers.dockmon-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.dockmon-dev.tls=true"
|
||||
|
||||
# Glance Metadata
|
||||
- glance.name=dockmon
|
||||
- glance.url=https://dockmon.forust.xyz/
|
||||
- glance.description=Dockmon is a lightweight Docker container monitoring and management tool with a user-friendly web interface.
|
||||
networks:
|
||||
- proxy
|
||||
# Glance Metadata
|
||||
- glance.name=dockmon
|
||||
- glance.url=https://dockmon.forust.xyz/
|
||||
- glance.description=Dockmon is a lightweight Docker container monitoring and management tool with a user-friendly web interface.
|
||||
networks:
|
||||
- proxy
|
||||
volumes:
|
||||
data:
|
||||
data:
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
proxy:
|
||||
external: true
|
||||
|
||||
@@ -15,9 +15,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^dockmon\.forust\.xyz$`)
|
||||
- match: Host(`dockmon.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-crowdsec-bouncer@kubernetescrd
|
||||
- name: security-headers@file
|
||||
services:
|
||||
- name: dockmon-service
|
||||
@@ -35,10 +36,9 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^dockmon\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`dockmon.workstation.internal`) || Host(`dockmon.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: dockmon-service
|
||||
port: 443
|
||||
serversTransport: dockmon-transport
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@ services:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.downtify.loadbalancer.server.port=8000"
|
||||
|
||||
# Prod Router
|
||||
# Prod Router
|
||||
- "traefik.http.routers.downtify.rule=Host(`downtify.forust.xyz`)"
|
||||
- "traefik.http.routers.downtify.entrypoints=websecure"
|
||||
- "traefik.http.routers.downtify.middlewares=security-chain@file"
|
||||
- "traefik.http.routers.downtify.tls=true"
|
||||
- "traefik.http.routers.downtify.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.downtify-local.rule=Host(`downtify.workstation.internal`)"
|
||||
- "traefik.http.routers.downtify-local.entrypoints=websecure"
|
||||
|
||||
@@ -7,9 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^downtify\.forust\.xyz$`)
|
||||
- match: Host(`downtify.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-crowdsec-bouncer@kubernetescrd
|
||||
- name: security-chain@file
|
||||
services:
|
||||
- name: downtify-service
|
||||
@@ -26,9 +27,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^downtify\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`downtify.workstation.internal`) || Host(`downtify.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: downtify-service
|
||||
port: 8000
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli", "ping" ]
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
@@ -25,7 +25,7 @@ services:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "redis-cli -h redis EXISTS EDU_PHPSESSID | grep -q 1" ]
|
||||
test: ["CMD-SHELL", "redis-cli -h redis EXISTS EDU_PHPSESSID | grep -q 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
+2
-3
@@ -12,7 +12,7 @@ services:
|
||||
- GITEA__database__USER=gitea
|
||||
- GITEA__database__PASSWD=gitea
|
||||
- GITEA__database__NAME=gitea
|
||||
#Server
|
||||
# Server
|
||||
- GITEA__server__ROOT_URL=https://gitea.forust.xyz
|
||||
- GITEA__server__SSH_DOMAIN=gitssh.forust.xyz
|
||||
- GITEA__server__SSH_PORT=2221
|
||||
@@ -36,7 +36,7 @@ services:
|
||||
# Prod Router
|
||||
- "traefik.http.routers.gitea.rule=Host(`gitea.forust.xyz`)"
|
||||
- "traefik.http.routers.gitea.entrypoints=websecure"
|
||||
- "traefik.http.routers.gitea.tls=true"
|
||||
- "traefik.http.routers.gitea.tls.certresolver"
|
||||
# Local Router
|
||||
- "traefik.http.routers.gitea-local.rule=Host(`gitea.workstation.internal`)"
|
||||
- "traefik.http.routers.gitea-local.entrypoints=websecure"
|
||||
@@ -53,7 +53,6 @@ services:
|
||||
- "traefik.http.routers.gitea-registry.rule=Host(`gcr.forust.xyz`) && PathPrefix(`/v2`)"
|
||||
- "traefik.http.routers.gitea-registry.entrypoints=websecure"
|
||||
- "traefik.http.routers.gitea-registry.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.gitea-registry.tls=true"
|
||||
ports:
|
||||
- "2221:22"
|
||||
networks:
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^gitea\.forust\.xyz$`)
|
||||
- match: Host(`gitea.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
services:
|
||||
- name: gitea-service
|
||||
port: 3000
|
||||
@@ -24,12 +26,11 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^gitea\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`gitea.workstation.internal`) || Host(`gitea.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: gitea-service
|
||||
port: 3000
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
+8
-5
@@ -15,19 +15,22 @@ services:
|
||||
- "traefik.services.glance.loadbalancer.server.port=8080"
|
||||
|
||||
# Prod Router
|
||||
- "traefik.http.routers.glance.rule=Host(`glance.forust.xyz`)"
|
||||
- "traefik.http.routers.glance.rule=(Host(`forust.xyz`) || Host(`www.forust.xyz`)) && PathPrefix(`/glance`)"
|
||||
- "traefik.http.routers.glance.entrypoints=websecure"
|
||||
- "traefik.http.routers.glance.tls=true"
|
||||
- "traefik.http.routers.glance.priority=50"
|
||||
- "traefik.http.routers.glance.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.glance-local.rule=Host(`glance.workstation.internal`)"
|
||||
- "traefik.http.routers.glance-local.rule=Host(`workstation.internal`) && PathPrefix(`/glance`)"
|
||||
- "traefik.http.routers.glance-local.entrypoints=websecure"
|
||||
- "traefik.http.routers.glance-local.priority=50"
|
||||
- "traefik.http.routers.glance-local.tls=true"
|
||||
# Dev Router
|
||||
- "traefik.http.routers.glance-dev.rule=Host(`glance.gigaforust.internal`)"
|
||||
- "traefik.http.routers.glance-dev.rule=Host(`gigaforust.internal`) && PathPrefix(`/glance`)"
|
||||
- "traefik.http.routers.glance-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.glance-dev.priority=50"
|
||||
- "traefik.http.routers.glance-dev.tls=true"
|
||||
networks:
|
||||
- proxy
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
external: true
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
name: Apple
|
||||
- symbol: MSFT
|
||||
name: Microsoft
|
||||
|
||||
|
||||
- type: releases
|
||||
cache: 1d
|
||||
|
||||
+11
-13
@@ -1,15 +1,13 @@
|
||||
- name: Monitoring
|
||||
columns:
|
||||
- size: small
|
||||
widgets:
|
||||
- type: dns-stats
|
||||
service: adguard
|
||||
url: http://adguardhome:3000
|
||||
username: forust
|
||||
password: ${ADGUARD_PASSWORD}
|
||||
- size: full
|
||||
widgets:
|
||||
- type: docker-containers
|
||||
hide-by-default: false
|
||||
|
||||
|
||||
- size: small
|
||||
widgets:
|
||||
- type: dns-stats
|
||||
service: adguard
|
||||
url: http://adguardhome:3000
|
||||
username: forust
|
||||
password: ${ADGUARD_PASSWORD}
|
||||
- size: full
|
||||
widgets:
|
||||
- type: docker-containers
|
||||
hide-by-default: false
|
||||
|
||||
@@ -56,6 +56,8 @@ data:
|
||||
glance.yml: |
|
||||
server:
|
||||
assets-path: /app/assets
|
||||
proxied: true
|
||||
base-url: /glance
|
||||
theme:
|
||||
# Перевели #050505 и #e0e0e0 в формат HSL для Glance
|
||||
background-color: 0 0 2 # Истинно черный фон
|
||||
|
||||
@@ -56,7 +56,7 @@ spec:
|
||||
readOnly: true
|
||||
resources:
|
||||
requests:
|
||||
memory: "10Mi"
|
||||
memory: "30Mi"
|
||||
cpu: "20m"
|
||||
limits:
|
||||
memory: "100Mi"
|
||||
|
||||
+17
-5
@@ -7,10 +7,11 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^forust\.xyz$`)
|
||||
- match: (Host(`forust.xyz`) || Host(`www.forust.xyz`)) && PathPrefix(`/glance`)
|
||||
kind: Rule
|
||||
priority: 50
|
||||
middlewares:
|
||||
- name: glance-strupprefix
|
||||
- name: glance-stripprefix
|
||||
services:
|
||||
- name: glance-service
|
||||
port: 8080
|
||||
@@ -26,10 +27,21 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^glance\.(workstation|gigaforust)\.internal$`)
|
||||
- match: (Host(`glance.workstation.internal`) || Host(`glance.gigaforust.internal`)) && PathPrefix(`/glance`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: glance-stripprefix
|
||||
priority: 50
|
||||
services:
|
||||
- name: glance-service
|
||||
port: 8080
|
||||
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: glance-stripprefix
|
||||
namespace: glance
|
||||
spec:
|
||||
stripPrefix:
|
||||
prefixes:
|
||||
- /glance
|
||||
|
||||
@@ -24,7 +24,7 @@ services:
|
||||
- "traefik.http.routers.headscale.rule=Host(`hs.forust.xyz`)"
|
||||
- "traefik.http.routers.headscale.entrypoints=websecure"
|
||||
- "traefik.http.routers.headscale.service=headscale"
|
||||
- "traefik.http.routers.headscale.tls=true"
|
||||
- "traefik.http.routers.headscale.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.headscale-local.rule=Host(`hs.workstation.internal`)"
|
||||
- "traefik.http.routers.headscale-local.entrypoints=websecure"
|
||||
@@ -57,7 +57,7 @@ services:
|
||||
container_name: headplane
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '13000:3000'
|
||||
- "13000:3000"
|
||||
volumes:
|
||||
- ./config/headplane.yaml:/etc/headplane/config.yaml
|
||||
- ./config/headscale.yaml:/etc/headscale/config.yaml
|
||||
@@ -78,7 +78,7 @@ services:
|
||||
- "traefik.http.routers.headscale-ui.rule=Host(`hs.forust.xyz`) && PathPrefix(`/admin`)"
|
||||
- "traefik.http.routers.headscale-ui.entrypoints=websecure"
|
||||
- "traefik.http.routers.headscale-ui.middlewares=security-chain@file"
|
||||
- "traefik.http.routers.headscale-ui.tls=true"
|
||||
- "traefik.http.routers.headscale-ui.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.headscale-ui-local.rule=Host(`hs.workstation.internal`) && PathPrefix(`/admin`)"
|
||||
- "traefik.http.routers.headscale-ui-local.entrypoints=websecure"
|
||||
|
||||
@@ -33,7 +33,9 @@ derp:
|
||||
auto_update_enabled: true
|
||||
update_frequency: 24h
|
||||
disable_check_updates: false
|
||||
ephemeral_node_inactivity_timeout: 30m
|
||||
node:
|
||||
ephemeral:
|
||||
inactivity_timeout: 30m
|
||||
database:
|
||||
type: sqlite
|
||||
debug: false
|
||||
@@ -76,4 +78,3 @@ unix_socket: /var/run/headscale/headscale.sock
|
||||
unix_socket_permission: "0770"
|
||||
logtail:
|
||||
enabled: false
|
||||
randomize_client_port: false
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"tagOwners": {},
|
||||
"hosts": {},
|
||||
"acls": [
|
||||
{
|
||||
{
|
||||
"randomizeClientPort": false,
|
||||
"#ha-meta": {
|
||||
"name": "users",
|
||||
"open": true
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^hs\.forust\.xyz$`)
|
||||
- match: Host(`hs.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-crowdsec-bouncer@kubernetescrd
|
||||
services:
|
||||
- name: headscale-server-external
|
||||
port: 8080
|
||||
@@ -24,7 +26,7 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^hs\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`hs.workstation.internal`) || Host(`hs.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: headscale-server-external
|
||||
@@ -40,9 +42,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^hs\.forust\.xyz$`) && PathPrefix(`/admin`)
|
||||
- match: Host(`hs.forust.xyz`) && PathPrefix(`/admin`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-crowdsec-bouncer@kubernetescrd
|
||||
- name: security-chain@file
|
||||
services:
|
||||
- name: headscale-ui-external
|
||||
@@ -75,13 +78,17 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^hs\.forust\.xyz$`) && PathPrefix(`/metrics`)
|
||||
- match: Host(`hs.forust.xyz`) && PathPrefix(`/metrics`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-crowdsec-bouncer@kubernetescrd
|
||||
services:
|
||||
- name: headscale-server-external
|
||||
port: 9090
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
domains:
|
||||
- main: hs.forust.xyz
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
@@ -97,5 +104,3 @@ spec:
|
||||
services:
|
||||
- name: headscale-server-external
|
||||
port: 9090
|
||||
|
||||
|
||||
|
||||
@@ -19,14 +19,17 @@ services:
|
||||
# Prod Router
|
||||
- "traefik.http.routers.forust-homepage.rule=Host(`forust.xyz`) || Host(`www.forust.xyz`)"
|
||||
- "traefik.http.routers.forust-homepage.entrypoints=websecure"
|
||||
- "traefik.http.routers.forust-homepage.tls=true"
|
||||
- "traefik.http.routers.forust-homepage.priority=10"
|
||||
- "traefik.http.routers.forust-homepage.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.forust-homepage-local.rule=Host(`landing.workstation.internal`)"
|
||||
- "traefik.http.routers.forust-homepage-local.entrypoints=websecure"
|
||||
- "traefik.http.routers.forust-homepage-local.priority=10"
|
||||
- "traefik.http.routers.forust-homepage-local.tls=true"
|
||||
# Dev Router
|
||||
- "traefik.http.routers.forust-homepage-dev.rule=Host(`landing.gigaforust.internal`)"
|
||||
- "traefik.http.routers.forust-homepage-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.forust-homepage-dev.priority=10"
|
||||
- "traefik.http.routers.forust-homepage-dev.tls=true"
|
||||
xdfnx:
|
||||
build:
|
||||
@@ -47,7 +50,6 @@ services:
|
||||
- "traefik.http.routers.xdfnx.rule=Host(`xdfnx.cfd`) || Host(`www.xdfnx.cfd`)"
|
||||
- "traefik.http.routers.xdfnx.entrypoints=websecure"
|
||||
- "traefik.http.routers.xdfnx.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.xdfnx.tls=true"
|
||||
# Local Router
|
||||
- "traefik.http.routers.xdfnx-local.rule=Host(`xdfnx.workstation.internal`)"
|
||||
- "traefik.http.routers.xdfnx-local.entrypoints=websecure"
|
||||
|
||||
@@ -20,49 +20,61 @@
|
||||
|
||||
<hr>
|
||||
|
||||
<section id="socials">
|
||||
<h2>./socials</h2>
|
||||
<ul class="link-list">
|
||||
<li>
|
||||
<i class="fab fa-github"></i>
|
||||
<a href="https://github.com/mr-forust" target="_blank">github/mr-forust</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-flag"></i>
|
||||
<a href="https://tryhackme.com/p/MrForust" target="_blank">tryhackme/MrForust</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fab fa-telegram-plane"></i>
|
||||
<a href="https://t.me/MrForust" target="_blank">telegram/MrForust</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fab fa-discord"></i>
|
||||
<span>discord/mr.forust</span>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-envelope"></i>
|
||||
<a href="mailto:contact@forust.xyz">mail/contact@forust.xyz</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-pie-chart"></i>
|
||||
<a href="https://forust.xyz/glance">forust/dashboard</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-refresh"></i>
|
||||
<a href="https://forust.xyz/convert">forust/converter</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-solid fa-key"></i>
|
||||
<a href=".well-known/pgp-key.asc">security/PGP Key</a>
|
||||
</li>
|
||||
<li>
|
||||
<p class="comment"># PGP Key Fingerprint: A777 7CB7 D9C4 0A97 443D CCF0 7A3D A455 F820 5B82</p>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<div class="grid-2">
|
||||
<section id="socials">
|
||||
<h2>./socials</h2>
|
||||
<ul class="link-list">
|
||||
<li>
|
||||
<i class="fab fa-github"></i>
|
||||
<a href="https://github.com/mr-forust" target="_blank">github/mr-forust</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-flag"></i>
|
||||
<a href="https://tryhackme.com/p/MrForust" target="_blank">tryhackme/MrForust</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fab fa-telegram-plane"></i>
|
||||
<a href="https://t.me/MrForust" target="_blank">telegram/MrForust</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fab fa-discord"></i>
|
||||
<span>discord/mr.forust</span>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fas fa-envelope"></i>
|
||||
<a href="mailto:contact@forust.xyz">mail/contact@forust.xyz</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-solid fa-key"></i>
|
||||
<a href=".well-known/pgp-key.asc">security/PGP Key</a>
|
||||
</li>
|
||||
<li>
|
||||
<p class="comment"># PGP Key Fingerprint: A777 7CB7 D9C4 0A97 443D CCF0 7A3D A455 F820 5B82</p>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="tools">
|
||||
<h2>./tools</h2>
|
||||
<ul class="link-list">
|
||||
<li>
|
||||
<i class="fa fa-pie-chart"></i>
|
||||
<a href="https://forust.xyz/glance" target="_blank">forust/dashboard</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-refresh"></i>
|
||||
<a href="https://forust.xyz/convert" target="_blank">forust/converter</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa-solid fa-file-pdf"></i>
|
||||
<a href="https://pdf.forust.xyz" target="_blank">pdf.forust.xyz</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section id="stack">
|
||||
<h2>./skills_and_tools</h2>
|
||||
<h2>./skills</h2>
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
<div class="skill-item">
|
||||
@@ -168,6 +180,10 @@
|
||||
<a href="https://github.com/unhappychoice/gitlogue" target="_blank">unhappychoice/gitlogue</a>
|
||||
<span class="comment">// nice git log visualizer</span>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/tstack/lnav" target="_blank">tstack/lnav</a>
|
||||
<span class="comment">// powerful log reader</span>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/mountain-loop/yaak" target="_blank">mountain-loop/yaak</a>
|
||||
<span class="comment">// modern, fancy api client</span>
|
||||
|
||||
@@ -28,6 +28,7 @@ spec:
|
||||
containers:
|
||||
- name: forust-homepage
|
||||
image: gcr.forust.xyz/forust/forust-homepage:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 80
|
||||
resources:
|
||||
@@ -68,6 +69,7 @@ spec:
|
||||
containers:
|
||||
- name: xdfnx-homepage
|
||||
image: gcr.forust.xyz/forust/xdfnx-homepage:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 80
|
||||
resources:
|
||||
|
||||
@@ -7,8 +7,11 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^(forust\.xyz|www\.forust\.xyz)$`)
|
||||
- match: Host(`forust.xyz`) || Host(`www.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-crowdsec-bouncer@kubernetescrd
|
||||
priority: 10
|
||||
services:
|
||||
- name: forust-homepage-service
|
||||
port: 80
|
||||
@@ -24,8 +27,9 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^landing\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`landing.workstation.internal`) || Host(`landing.gigaforust.internal`)
|
||||
kind: Rule
|
||||
priority: 10
|
||||
services:
|
||||
- name: forust-homepage-service
|
||||
port: 80
|
||||
@@ -39,8 +43,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^(xdfnx\.cfd|www\.xdfnx\.cfd)$`)
|
||||
- match: Host(`xdfnx.cfd`) || Host(`www.xdfnx.cfd`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-crowdsec-bouncer@kubernetescrd
|
||||
services:
|
||||
- name: xdfnx-homepage-service
|
||||
port: 80
|
||||
@@ -56,9 +62,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^xdfnx\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`xdfnx.workstation.internal`) || Host(`xdfnx.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: xdfnx-homepage-service
|
||||
port: 80
|
||||
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ services:
|
||||
# Prod Router
|
||||
- "traefik.http.routers.kener.rule=Host(`status.forust.xyz`)"
|
||||
- "traefik.http.routers.kener.entrypoints=websecure"
|
||||
- "traefik.http.routers.kener.tls=true"
|
||||
- "traefik.http.routers.kener.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.kener-local.rule=Host(`status.workstation.internal`)"
|
||||
- "traefik.http.routers.kener-local.entrypoints=websecure"
|
||||
@@ -42,7 +42,7 @@ services:
|
||||
volumes:
|
||||
- redis:/data
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli", "ping" ]
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 6s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^status\.forust\.xyz$`)
|
||||
- match: Host(`status.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-crowdsec-bouncer@kubernetescrd
|
||||
services:
|
||||
- name: kener-service
|
||||
port: 3000
|
||||
@@ -24,9 +26,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^status\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`status.workstation.internal`) || Host(`status.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: kener-service
|
||||
port: 3000
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: kener
|
||||
image: rajnandan1/kener:4.0.23
|
||||
image: rajnandan1/kener:4.1.0
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: kener-config
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ services:
|
||||
- "traefik.http.routers.metube.rule=Host(`metube.forust.xyz`)"
|
||||
- "traefik.http.routers.metube.entrypoints=websecure"
|
||||
- "traefik.http.routers.metube.middlewares=security-chain@file"
|
||||
- "traefik.http.routers.metube.tls=true"
|
||||
- "traefik.http.routers.metube.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.metube-local.rule=Host(`metube.workstation.internal`)"
|
||||
- "traefik.http.routers.metube-local.entrypoints=websecure"
|
||||
|
||||
@@ -7,7 +7,7 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^metube\.forust\.xyz$`)
|
||||
- match: Host(`metube.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: security-headers@file
|
||||
@@ -26,9 +26,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^metube\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`metube.workstation.internal`) || Host(`metube.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: metube-service
|
||||
port: 8081
|
||||
|
||||
|
||||
+2
-2
@@ -32,7 +32,7 @@ services:
|
||||
# Prod Router
|
||||
- "traefik.http.routers.n8n.rule=Host(`n8n.forust.xyz`)"
|
||||
- "traefik.http.routers.n8n.entrypoints=websecure"
|
||||
- "traefik.http.routers.n8n.tls=true"
|
||||
- "traefik.http.routers.n8n.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.n8n-local.rule=Host(`n8n.workstation.internal`)"
|
||||
- "traefik.http.routers.n8n-local.entrypoints=websecure"
|
||||
@@ -54,4 +54,4 @@ networks:
|
||||
|
||||
volumes:
|
||||
node-data:
|
||||
files:
|
||||
files:
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^n8n\.forust\.xyz$`)
|
||||
- match: Host(`n8n.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
services:
|
||||
- name: n8n-service
|
||||
port: 5678
|
||||
@@ -24,9 +26,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^n8n\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`n8n.workstation.internal`) || Host(`n8n.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: n8n-service
|
||||
port: 5678
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ services:
|
||||
# Prod Router
|
||||
- "traefik.http.routers.netronome.rule=Host(`nm.forust.xyz`)"
|
||||
- "traefik.http.routers.netronome.entrypoints=websecure"
|
||||
- "traefik.http.routers.netronome.tls=true"
|
||||
- "traefik.http.routers.netronome.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.netronome-local.rule=Host(`nm.workstation.internal`)"
|
||||
- "traefik.http.routers.netronome-local.entrypoints=websecure"
|
||||
@@ -41,7 +41,7 @@ services:
|
||||
volumes:
|
||||
- data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready -U netronome" ]
|
||||
test: ["CMD-SHELL", "pg_isready -U netronome"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
@@ -51,7 +51,6 @@ services:
|
||||
volumes:
|
||||
data:
|
||||
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^nm\.forust\.xyz$`)
|
||||
- match: Host(`nm.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
services:
|
||||
- name: netronome-service
|
||||
port: 7575
|
||||
@@ -24,9 +26,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^nm\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`nm.workstation.internal`) || Host(`nm.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: netronome-service
|
||||
port: 7575
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
restart: unless-stopped
|
||||
container_name: nextcloud-aio-mastercontainer # Do not change
|
||||
volumes:
|
||||
- nextcloud_aio_mastercontainer:/mnt/docker-aio-config # Do not change (backup)
|
||||
- nextcloud_aio_mastercontainer:/mnt/docker-aio-config # Do not change (backup)
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /dev/dri:/dev/dri
|
||||
ports:
|
||||
@@ -25,7 +25,7 @@ services:
|
||||
# - "traefik.http.routers.nextcloud-aio.rule=Host(`naio.forust.xyz`)"
|
||||
# - "traefik.http.routers.nextcloud-aio.entrypoints=websecure"
|
||||
# - "traefik.http.routers.nextcloud-aio.middlewares=security-chain@file"
|
||||
# - "traefik.http.routers.nextcloud-aio.tls=true"
|
||||
# - "traefik.http.routers.nextcloud-aio.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.nextcloud-aio-local.rule=Host(`naio.workstation.internal`)"
|
||||
- "traefik.http.routers.nextcloud-aio-local.entrypoints=websecure"
|
||||
|
||||
+24
-24
@@ -8,10 +8,11 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^nextcloud\.forust\.xyz$`)
|
||||
- match: Host(`nextcloud.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: nextcloud-chain@file
|
||||
- name: crowdsec-crowdsec-bouncer@kubernetescrd
|
||||
services:
|
||||
- name: nextcloud-apache
|
||||
port: 11000
|
||||
@@ -27,34 +28,34 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^nextcloud\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`nextcloud.workstation.internal`) || Host(`nextcloud.gigaforust.internal`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
- name: nextcloud-chain@file
|
||||
services:
|
||||
- name: nextcloud-apache
|
||||
port: 11000
|
||||
|
||||
---
|
||||
# ---
|
||||
# Nextcloud AIO
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: naio-prod
|
||||
namespace: nextcloud
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^naio\.forust\.xyz$`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: nextcloud-aio
|
||||
port: 8888
|
||||
scheme: https
|
||||
serversTransport: insecure-transport # <-- Ссылка на ваш CRD ресурс вместо @file
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
# apiVersion: traefik.io/v1alpha1
|
||||
# kind: IngressRoute
|
||||
# metadata:
|
||||
# name: naio-prod
|
||||
# namespace: nextcloud
|
||||
# spec:
|
||||
# entryPoints:
|
||||
# - websecure
|
||||
# routes:
|
||||
# - match: Host(`naio.forust.xyz`)
|
||||
# kind: Rule
|
||||
# services:
|
||||
# - name: nextcloud-aio
|
||||
# port: 8888
|
||||
# scheme: https
|
||||
# serversTransport: insecure-transport
|
||||
# tls:
|
||||
# certResolver: letsencrypt
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
@@ -65,11 +66,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^naio\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`naio.workstation.internal`) || Host(`naio.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: nextcloud-aio
|
||||
port: 8888
|
||||
scheme: https
|
||||
serversTransport: insecure-transport
|
||||
|
||||
|
||||
+6
-6
@@ -107,7 +107,7 @@ services:
|
||||
# Prod Router
|
||||
- "traefik.http.routers.penpot.rule=Host(`penpot.forust.xyz`)"
|
||||
- "traefik.http.routers.penpot.entrypoints=websecure"
|
||||
- "traefik.http.routers.penpot.tls=true"
|
||||
- "traefik.http.routers.penpot.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.penpot-local.rule=Host(`penpot.workstation.internal`)"
|
||||
- "traefik.http.routers.penpot-local.entrypoints=websecure"
|
||||
@@ -117,7 +117,7 @@ services:
|
||||
- "traefik.http.routers.penpot-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.penpot-dev.tls=true"
|
||||
environment:
|
||||
<<: [ *penpot-flags, *penpot-http-body-size ]
|
||||
<<: [*penpot-flags, *penpot-http-body-size]
|
||||
penpot-backend:
|
||||
image: "penpotapp/backend:${PENPOT_VERSION:-latest}"
|
||||
restart: always
|
||||
@@ -137,7 +137,7 @@ services:
|
||||
## Configuration envronment variables for the backend container.
|
||||
|
||||
environment:
|
||||
<<: [ *penpot-flags, *penpot-public-uri, *penpot-http-body-size, *penpot-secret-key ]
|
||||
<<: [*penpot-flags, *penpot-public-uri, *penpot-http-body-size, *penpot-secret-key]
|
||||
|
||||
## The PREPL host. Mainly used for external programatic access to penpot backend
|
||||
## (example: admin). By default it will listen on `localhost` but if you are going to use
|
||||
@@ -200,7 +200,7 @@ services:
|
||||
- penpot
|
||||
|
||||
environment:
|
||||
<<: [ *penpot-secret-key ]
|
||||
<<: [*penpot-secret-key]
|
||||
# Don't touch it; this uses an internal docker network to
|
||||
# communicate with the frontend.
|
||||
PENPOT_PUBLIC_URI: http://penpot-frontend:8080
|
||||
@@ -214,7 +214,7 @@ services:
|
||||
stop_signal: SIGINT
|
||||
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready -U penpot" ]
|
||||
test: ["CMD-SHELL", "pg_isready -U penpot"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
@@ -237,7 +237,7 @@ services:
|
||||
restart: always
|
||||
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "valkey-cli ping | grep PONG" ]
|
||||
test: ["CMD-SHELL", "valkey-cli ping | grep PONG"]
|
||||
interval: 1s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
@@ -16,7 +16,7 @@ services:
|
||||
# Prod Router
|
||||
- "traefik.http.routers.portainer.rule=Host(`portainer.forust.xyz`)"
|
||||
- "traefik.http.routers.portainer.entrypoints=websecure"
|
||||
- "traefik.http.routers.portainer.tls=true"
|
||||
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.portainer-local.rule=Host(`portainer.workstation.internal`)"
|
||||
- "traefik.http.routers.portainer-local.entrypoints=websecure"
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^portainer\.forust\.xyz$`)
|
||||
- match: Host(`portainer.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
services:
|
||||
- name: portainer-service
|
||||
port: 9000
|
||||
@@ -24,9 +26,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^portainer\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`portainer.workstation.internal`) || Host(`portainer.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: portainer-service
|
||||
port: 9000
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
GRAFANA_ADMIN_USER=admin
|
||||
GRAFANA_ADMIN_PASSWORD=changeme
|
||||
@@ -0,0 +1,5 @@
|
||||
route:
|
||||
receiver: default
|
||||
|
||||
receivers:
|
||||
- name: default
|
||||
@@ -0,0 +1,66 @@
|
||||
services:
|
||||
grafana:
|
||||
image: grafana/grafana:11.6.0
|
||||
container_name: prometheus-grafana
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin}
|
||||
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:?grafana admin password required}
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.prometheus-grafana.loadbalancer.server.port=3000"
|
||||
|
||||
# Prod Router
|
||||
- "traefik.http.routers.grafana.rule=Host(`grafana.forust.xyz`)"
|
||||
- "traefik.http.routers.grafana.entrypoints=websecure"
|
||||
- "traefik.http.routers.grafana.tls=true"
|
||||
# Local Router
|
||||
- "traefik.http.routers.grafana-local.rule=Host(`grafana.workstation.internal`)"
|
||||
- "traefik.http.routers.grafana-local.entrypoints=websecure"
|
||||
- "traefik.http.routers.grafana-local.tls=true"
|
||||
# Dev Router
|
||||
- "traefik.http.routers.grafana-dev.rule=Host(`grafana.gigaforust.internal`)"
|
||||
- "traefik.http.routers.grafana-dev.entrypoints=websecure"
|
||||
- "traefik.http.routers.grafana-dev.tls=true"
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus:v3.2.1
|
||||
container_name: prometheus-prometheus
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- "--config.file=/etc/prometheus/prometheus.yaml"
|
||||
- "--storage.tsdb.path=/prometheus"
|
||||
- "--storage.tsdb.retention.time=90d"
|
||||
volumes:
|
||||
- prometheus-data:/prometheus
|
||||
- ./prometheus.yaml:/etc/prometheus/prometheus.yaml:ro
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
alertmanager:
|
||||
image: prom/alertmanager:v0.28.1
|
||||
container_name: prometheus-alertmanager
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- "--config.file=/etc/alertmanager/alertmanager.yaml"
|
||||
- "--storage.path=/alertmanager"
|
||||
volumes:
|
||||
- alertmanager-data:/alertmanager
|
||||
- ./alertmanager.yaml:/etc/alertmanager/alertmanager.yaml:ro
|
||||
networks:
|
||||
- proxy
|
||||
|
||||
volumes:
|
||||
grafana-data:
|
||||
prometheus-data:
|
||||
alertmanager-data:
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
@@ -0,0 +1,52 @@
|
||||
grafana:
|
||||
grafana.ini:
|
||||
server:
|
||||
domain: grafana.forust.xyz
|
||||
root_url: https://grafana.forust.xyz
|
||||
auth.anonymous:
|
||||
enabled: false
|
||||
users:
|
||||
allow_sign_up: false
|
||||
admin:
|
||||
existingSecret: grafana-admin
|
||||
userKey: admin-user
|
||||
passwordKey: admin-password
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 10Gi
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
|
||||
service:
|
||||
port: 80
|
||||
|
||||
prometheus:
|
||||
prometheusSpec:
|
||||
storageSpec:
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
storageClassName: "local-path"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 40Gi
|
||||
resources:
|
||||
requests:
|
||||
memory: "700Mi"
|
||||
cpu: 200m
|
||||
limits:
|
||||
memory: "2Gi"
|
||||
alertmanager:
|
||||
alertmanagerSpec:
|
||||
storage:
|
||||
volumeClaimTemplate:
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
|
||||
resources:
|
||||
requests:
|
||||
storage: 20Gi
|
||||
@@ -0,0 +1,36 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: grafana-prod
|
||||
namespace: prometheus
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`grafana.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
- name: "security-chain@file"
|
||||
services:
|
||||
- name: prometheus-stack-grafana
|
||||
port: 80
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
domains:
|
||||
- main: grafana.forust.xyz
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: grafana-local
|
||||
namespace: prometheus
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`grafana.workstation.internal`) || Host(`grafana.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: prometheus-stack-grafana
|
||||
port: 80
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: prometheus
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: grafana-admin
|
||||
namespace: prometheus
|
||||
type: Opaque
|
||||
stringData:
|
||||
admin-user: "admin"
|
||||
admin-password: ""
|
||||
@@ -0,0 +1,8 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: prometheus
|
||||
static_configs:
|
||||
- targets: ["localhost:9090"]
|
||||
@@ -0,0 +1,11 @@
|
||||
target-version = "py311"
|
||||
line-length = 120
|
||||
|
||||
[lint]
|
||||
select = ["E", "F", "W", "I", "N", "UP", "S", "B", "A", "C4", "SIM", "ARG"]
|
||||
ignore = ["E501", "S101"]
|
||||
|
||||
[format]
|
||||
quote-style = "single"
|
||||
indent-style = "space"
|
||||
line-ending = "lf"
|
||||
@@ -6,15 +6,15 @@ services:
|
||||
image: docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}
|
||||
restart: unless-stopped
|
||||
# ports:
|
||||
# - ${SEARXNG_PORT:-8080}
|
||||
# - ${SEARXNG_PORT:-8080}
|
||||
env_file: .env
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.searxng.loadbalancer.server.port=8080"
|
||||
# Prod Router
|
||||
- "traefik.http.routers.searxng.rule=Host(`s.forust.xyz` || `searxng.forust.xyz`)"
|
||||
- "traefik.http.routers.searxng.rule=Host(`s.forust.xyz` || `search.forust.xyz`)"
|
||||
- "traefik.http.routers.searxng.entrypoints=websecure"
|
||||
- "traefik.http.routers.searxng.tls=true"
|
||||
- "traefik.http.routers.searxng.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.searxng-local.rule=Host(`s.workstation.internal` || `searxng.workstation.internal`)"
|
||||
- "traefik.http.routers.searxng-local.entrypoints=websecure"
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^(s|searxng)\.forust\.xyz$`)
|
||||
- match: Host(`s.forust.xyz`) || Host(`search.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
services:
|
||||
- name: searxng-service
|
||||
port: 8080
|
||||
@@ -24,9 +26,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^(s|searxng)\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`s.workstation.internal`) || Host(`searxng.workstation.internal`) || Host(`s.gigaforust.internal`) || Host(`searxng.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: searxng-service
|
||||
port: 8080
|
||||
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ services:
|
||||
# Prod Router
|
||||
- "traefik.http.routers.termix.rule=Host(`termix.forust.xyz`)"
|
||||
- "traefik.http.routers.termix.entrypoints=websecure"
|
||||
- "traefik.http.routers.termix.tls=true"
|
||||
- "traefik.http.routers.termix.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.termix-local.rule=Host(`termix.workstation.internal`)"
|
||||
- "traefik.http.routers.termix-local.entrypoints=websecure"
|
||||
@@ -31,4 +31,4 @@ networks:
|
||||
proxy:
|
||||
external: true
|
||||
volumes:
|
||||
data:
|
||||
data:
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^termix\.forust\.xyz$`)
|
||||
- match: Host(`termix.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
services:
|
||||
- name: termix-service
|
||||
port: 8080
|
||||
@@ -24,9 +26,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^termix\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`termix.workstation.internal`) || Host(`termix.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: termix-service
|
||||
port: 8080
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ services:
|
||||
# # STAGING
|
||||
# - "--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
|
||||
# Cloudflare
|
||||
# Cloudflare
|
||||
- "--entryPoints.web.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22"
|
||||
- "--entryPoints.websecure.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22"
|
||||
# Logging
|
||||
@@ -49,7 +49,7 @@ services:
|
||||
- "traefik.http.routers.traefik-dashboard.entrypoints=websecure"
|
||||
- "traefik.http.routers.traefik-dashboard.middlewares=security-chain@file"
|
||||
- "traefik.http.routers.traefik-dashboard.service=api@internal"
|
||||
- "traefik.http.routers.traefik-dashboard.tls=true"
|
||||
- "traefik.http.routers.traefik-dashboard.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.traefik-dashboard-local.rule=Host(`traefik.workstation.internal`)"
|
||||
- "traefik.http.routers.traefik-dashboard-local.entrypoints=websecure"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
http:
|
||||
serversTransports:
|
||||
insecureTransport:
|
||||
insecureSkipVerify: true
|
||||
insecureSkipVerify: true
|
||||
|
||||
@@ -8,7 +8,8 @@ http:
|
||||
service: nextcloud
|
||||
middlewares:
|
||||
- nextcloud-chain
|
||||
tls: {}
|
||||
tls:
|
||||
certresolver: letsencrypt
|
||||
|
||||
# Nextcloud dev
|
||||
nextcloud-local:
|
||||
|
||||
@@ -8,8 +8,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^traefik\.forust\.xyz$`)
|
||||
- match: Host(`traefik.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
|
||||
services:
|
||||
- name: api@internal
|
||||
kind: TraefikService
|
||||
@@ -25,9 +27,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^traefik\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`traefik.workstation.internal`) || Host(`traefik.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: api@internal
|
||||
kind: TraefikService
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ data:
|
||||
- security-headers@file
|
||||
authentik:
|
||||
forwardAuth:
|
||||
address: "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik"
|
||||
address: "http://authentik-server-service.authentik.svc:9000/outpost.goauthentik.io/auth/traefik"
|
||||
trustForwardHeader: true
|
||||
maxResponseBodySize: 1048576
|
||||
authResponseHeaders:
|
||||
|
||||
@@ -13,6 +13,8 @@ service:
|
||||
type: LoadBalancer
|
||||
annotations:
|
||||
metallb.io/loadBalancerIPs: "192.168.80.2"
|
||||
spec:
|
||||
externalTrafficPolicy: Local
|
||||
api:
|
||||
dashboard: true
|
||||
insecure: true
|
||||
@@ -75,12 +77,22 @@ ports:
|
||||
protocol: UDP
|
||||
expose:
|
||||
default: true
|
||||
dot:
|
||||
port: 853
|
||||
exposedPort: 853
|
||||
metrics:
|
||||
port: 9100
|
||||
exposedPort: 9100
|
||||
protocol: TCP
|
||||
expose:
|
||||
default: true
|
||||
default: false
|
||||
|
||||
metrics:
|
||||
prometheus:
|
||||
enabled: true
|
||||
entryPoint: metrics
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
additionalLabels:
|
||||
release: prometheus-stack
|
||||
namespace: prometheus
|
||||
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
@@ -111,14 +123,26 @@ volumes:
|
||||
- name: traefik-dynamic
|
||||
mountPath: /etc/traefik/dynamic
|
||||
type: configMap
|
||||
- name: crowdsec-bouncer-secrets
|
||||
mountPath: /etc/traefik/secrets
|
||||
type: secret
|
||||
|
||||
additionalArguments:
|
||||
- "--providers.file.directory=/etc/traefik/dynamic"
|
||||
- "--providers.file.watch=true"
|
||||
- "--providers.kubernetesCRD.allowCrossNamespace=true"
|
||||
- "--entryPoints.websecure.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22,192.168.1.1,192.168.1.0/24,192.168.88.0/24,192.168.88.1"
|
||||
- "--entryPoints.web.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/13,104.24.0.0/14,172.64.0.0/13,131.0.72.0/22,192.168.1.1,192.168.1.0/24,192.168.88.0/24,192.168.88.1"
|
||||
|
||||
logs:
|
||||
general:
|
||||
level: INFO
|
||||
format: json
|
||||
access:
|
||||
enabled: true
|
||||
format: common
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
crowdsec-bouncer:
|
||||
moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
|
||||
version: v1.3.3
|
||||
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
# Prod Router
|
||||
- "traefik.http.routers.uptime-kuma.rule=Host(`uptime.forust.xyz`)"
|
||||
- "traefik.http.routers.uptime-kuma.entrypoints=websecure"
|
||||
- "traefik.http.routers.uptime-kuma.tls=true"
|
||||
- "traefik.http.routers.uptime-kuma.tls.certresolver=letsencrypt"
|
||||
# Local Router
|
||||
- "traefik.http.routers.uptime-kuma-local.rule=Host(`uptime.workstation.internal`)"
|
||||
- "traefik.http.routers.uptime-kuma-local.entrypoints=websecure"
|
||||
@@ -29,4 +29,4 @@ volumes:
|
||||
data:
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
external: true
|
||||
|
||||
@@ -7,8 +7,10 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^uptime\.forust\.xyz$`)
|
||||
- match: Host(`uptime.forust.xyz`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: crowdsec-crowdsec-bouncer@kubernetescrd
|
||||
services:
|
||||
- name: uptime-kuma-service
|
||||
port: 3001
|
||||
@@ -24,9 +26,8 @@ spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: HostRegexp(`^uptime\.(workstation|gigaforust)\.internal$`)
|
||||
- match: Host(`uptime.workstation.internal`) || Host(`uptime.gigaforust.internal`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: uptime-kuma-service
|
||||
port: 3001
|
||||
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
"name": "Moon-userbot",
|
||||
"description": "A Simple, Fast, Customizable, Ai powered Userbot for Telegram with most easiest installation.",
|
||||
"logo": "https://camo.githubusercontent.com/1efdfa6416b3cd08471d865ca9ebf0fbdd38602ea95425f18a9bec6aeeefe49b/68747470733a2f2f74656c656772612e70682f66696c652f3063333763326662306631393463633163303334342e6a7067",
|
||||
"keywords": [
|
||||
"telegram",
|
||||
"Moon-userbot",
|
||||
"bot",
|
||||
"python",
|
||||
"pyrogram"
|
||||
],
|
||||
"keywords": ["telegram", "Moon-userbot", "bot", "python", "pyrogram"],
|
||||
"env": {
|
||||
"API_ID": {
|
||||
"description": "Get it from my.telegram.org",
|
||||
|
||||
@@ -3,10 +3,10 @@ from flask import Flask
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@app.route('/')
|
||||
def hello_world():
|
||||
return "This is Moon"
|
||||
return 'This is Moon'
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
||||
+15
-15
@@ -1,17 +1,17 @@
|
||||
services:
|
||||
- type: worker
|
||||
name: Moon-Userbot
|
||||
runtime: docker
|
||||
repo: https://github.com/The-MoonTg-project/Moon-Userbot
|
||||
plan: starter
|
||||
envVars:
|
||||
- key: STABILITY_KEY
|
||||
sync: false
|
||||
- key: CLARIFAI_PAT
|
||||
sync: false
|
||||
- key: .env
|
||||
sync: false
|
||||
region: oregon
|
||||
dockerContext: .
|
||||
dockerfilePath: ./Dockerfile
|
||||
- type: worker
|
||||
name: Moon-Userbot
|
||||
runtime: docker
|
||||
repo: https://github.com/The-MoonTg-project/Moon-Userbot
|
||||
plan: starter
|
||||
envVars:
|
||||
- key: STABILITY_KEY
|
||||
sync: false
|
||||
- key: CLARIFAI_PAT
|
||||
sync: false
|
||||
- key: .env
|
||||
sync: false
|
||||
region: oregon
|
||||
dockerContext: .
|
||||
dockerfilePath: ./Dockerfile
|
||||
version: "1"
|
||||
|
||||
+19
-9
@@ -1,18 +1,28 @@
|
||||
FROM python:3.11-slim AS builder
|
||||
|
||||
WORKDIR /src
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir --user -r requirements.txt
|
||||
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git wget ffmpeg mediainfo && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
git \
|
||||
mediainfo \
|
||||
wget \
|
||||
xz-utils \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt /app/
|
||||
|
||||
RUN python -m pip install --no-cache-dir --upgrade pip && \
|
||||
python -m pip install --no-cache-dir -r /app/requirements.txt
|
||||
RUN wget -qO- https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz | \
|
||||
tar xJ && \
|
||||
cp ffmpeg-*-static/ffmpeg /usr/local/bin/ && \
|
||||
cp ffmpeg-*-static/ffprobe /usr/local/bin/ && \
|
||||
rm -rf ffmpeg-*
|
||||
|
||||
COPY --from=builder /root/.local /usr/local
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
CMD ["python", "-u", "main.py"]
|
||||
CMD ["python", "-u", "main.py"]
|
||||
|
||||
+36
-41
@@ -1,83 +1,80 @@
|
||||
import re
|
||||
import requests
|
||||
import random
|
||||
import re
|
||||
from io import BytesIO
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup as bs
|
||||
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.types import Message, InputMediaPhoto
|
||||
from pyrogram.errors import RPCError
|
||||
|
||||
from pyrogram.types import InputMediaPhoto, Message
|
||||
from utils.misc import modules_help, prefix
|
||||
|
||||
|
||||
@Client.on_message(filters.command("icon", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('icon', prefix) & filters.me)
|
||||
async def search_icon(_, message: Message):
|
||||
if not len(message.command) == 2:
|
||||
return await message.edit_text(
|
||||
"Please provide some text to search icons from Flaticon.com."
|
||||
)
|
||||
return await message.edit_text('Please provide some text to search icons from Flaticon.com.')
|
||||
query = message.text.split(maxsplit=1)[1]
|
||||
|
||||
await message.edit_text("Searching for icons...")
|
||||
search_query = query.replace(" ", "%20")
|
||||
url = f"https://www.flaticon.com/search?word={search_query}"
|
||||
await message.edit_text('Searching for icons...')
|
||||
search_query = query.replace(' ', '%20')
|
||||
url = f'https://www.flaticon.com/search?word={search_query}'
|
||||
|
||||
try:
|
||||
html_content = requests.get(url).text
|
||||
soup = bs(html_content, "html.parser")
|
||||
soup = bs(html_content, 'html.parser')
|
||||
results = soup.find_all(
|
||||
"img",
|
||||
src=re.compile(r"https://cdn-icons-png.flaticon.com/128/[0-9]+/[0-9]+.png"),
|
||||
'img',
|
||||
src=re.compile(r'https://cdn-icons-png.flaticon.com/128/[0-9]+/[0-9]+.png'),
|
||||
)
|
||||
|
||||
if not results:
|
||||
return await message.edit("No results found.")
|
||||
return await message.edit('No results found.')
|
||||
|
||||
random.shuffle(results)
|
||||
icons = []
|
||||
for i in range(5):
|
||||
icons.append(results[i]["src"].replace("128", "512"))
|
||||
icons.append(results[i]['src'].replace('128', '512'))
|
||||
|
||||
for icon in icons:
|
||||
await message.reply_document(icon)
|
||||
|
||||
return await message.delete()
|
||||
except Exception as e:
|
||||
await message.edit(f"An error occurred: {e}")
|
||||
print(f"Error: {e}")
|
||||
await message.edit(f'An error occurred: {e}')
|
||||
print(f'Error: {e}')
|
||||
|
||||
|
||||
@Client.on_message(filters.command("freepik", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('freepik', prefix) & filters.me)
|
||||
async def freepik_search(client: Client, message: Message):
|
||||
parts = message.text.split(" ", 1)
|
||||
parts = message.text.split(' ', 1)
|
||||
if len(parts) < 2:
|
||||
await message.edit_text("Please provide a search query!")
|
||||
await message.edit_text('Please provide a search query!')
|
||||
return
|
||||
|
||||
query = parts[1]
|
||||
limit = 5
|
||||
if " ; " in query:
|
||||
match, limit_str = query.split(" ; ", 1)
|
||||
if ' ; ' in query:
|
||||
match, limit_str = query.split(' ; ', 1)
|
||||
try:
|
||||
limit = int(limit_str)
|
||||
except ValueError:
|
||||
await message.edit_text("Invalid limit! Using the default value of 5.")
|
||||
await message.edit_text('Invalid limit! Using the default value of 5.')
|
||||
else:
|
||||
match = query
|
||||
|
||||
match = match.replace(" ", "%20")
|
||||
await message.edit_text("Searching Freepik...")
|
||||
match = match.replace(' ', '%20')
|
||||
await message.edit_text('Searching Freepik...')
|
||||
|
||||
try:
|
||||
url = f"https://www.freepik.com/api/regular/search?locale=en&term={match}"
|
||||
url = f'https://www.freepik.com/api/regular/search?locale=en&term={match}'
|
||||
json_content = requests.get(url).json()
|
||||
results = []
|
||||
for i in json_content["items"]:
|
||||
results.append(i["preview"]["url"])
|
||||
for i in json_content['items']:
|
||||
results.append(i['preview']['url'])
|
||||
|
||||
if results is None:
|
||||
return await message.edit_text("No results found.")
|
||||
return await message.edit_text('No results found.')
|
||||
|
||||
random.shuffle(results)
|
||||
img_urls = results[:limit]
|
||||
@@ -89,27 +86,25 @@ async def freepik_search(client: Client, message: Message):
|
||||
media_group.append(InputMediaPhoto(media=BytesIO(icon.content)))
|
||||
|
||||
if not media_group:
|
||||
await message.edit_text("No images could be downloaded.")
|
||||
await message.edit_text('No images could be downloaded.')
|
||||
return
|
||||
|
||||
try:
|
||||
await client.send_media_group(chat_id=message.chat.id, media=media_group)
|
||||
except RPCError:
|
||||
await message.edit_text(
|
||||
"Failed to send some images. Retrying individually..."
|
||||
)
|
||||
await message.edit_text('Failed to send some images. Retrying individually...')
|
||||
for media in media_group:
|
||||
try:
|
||||
await message.reply_photo(photo=media.media)
|
||||
except Exception as e:
|
||||
await message.edit_text(f"Error sending image: {e}")
|
||||
await message.edit_text(f'Error sending image: {e}')
|
||||
|
||||
except Exception as e:
|
||||
await message.edit_text(f"Failed to fetch data: {e}")
|
||||
print(f"Error: {e}")
|
||||
await message.edit_text(f'Failed to fetch data: {e}')
|
||||
print(f'Error: {e}')
|
||||
|
||||
|
||||
modules_help["icons"] = {
|
||||
"icon [query]": "Search for icons on Flaticon.",
|
||||
"freepik [query] [limit]": "Search for images on Freepik. Limit is optional and defaults to 5.",
|
||||
modules_help['icons'] = {
|
||||
'icon [query]': 'Search for icons on Flaticon.',
|
||||
'freepik [query] [limit]': 'Search for images on Freepik. Limit is optional and defaults to 5.',
|
||||
}
|
||||
|
||||
+16
-21
@@ -1,52 +1,47 @@
|
||||
import os
|
||||
import base64
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.types import Message
|
||||
|
||||
from utils.misc import modules_help, prefix
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["imgur"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['imgur'], prefix) & filters.me)
|
||||
async def imgur(_, message: Message):
|
||||
# Check if a reply exists
|
||||
msg = await message.edit_text("🎉 Please wait. trying to upload...")
|
||||
msg = await message.edit_text('🎉 Please wait. trying to upload...')
|
||||
if message.reply_to_message and message.reply_to_message.photo:
|
||||
# Download the photo
|
||||
photo_path = await message.reply_to_message.download()
|
||||
# Read the photo file and encode as base64
|
||||
with open(photo_path, "rb") as file:
|
||||
with open(photo_path, 'rb') as file:
|
||||
data = file.read()
|
||||
base64_data = base64.b64encode(data)
|
||||
# Set API endpoint and headers for image upload
|
||||
url = "https://api.imgur.com/3/image"
|
||||
headers = {"Authorization": "Client-ID a10ad04550b0648"}
|
||||
url = 'https://api.imgur.com/3/image'
|
||||
headers = {'Authorization': 'Client-ID a10ad04550b0648'}
|
||||
# Upload image to Imgur and get URL
|
||||
response = requests.post(url, headers=headers, data={"image": base64_data})
|
||||
response = requests.post(url, headers=headers, data={'image': base64_data})
|
||||
result = response.json()
|
||||
await msg.edit_text(result["data"]["link"])
|
||||
await msg.edit_text(result['data']['link'])
|
||||
elif message.reply_to_message and message.reply_to_message.animation:
|
||||
# Download the animation (GIF)
|
||||
animation_path = await message.reply_to_message.download()
|
||||
# Read the animation file and encode as base64
|
||||
with open(animation_path, "rb") as file:
|
||||
with open(animation_path, 'rb') as file:
|
||||
data = file.read()
|
||||
base64_data = base64.b64encode(data)
|
||||
# Set API endpoint and headers for animation upload
|
||||
url = "https://api.imgur.com/3/image"
|
||||
headers = {"Authorization": "Client-ID a10ad04550b0648"}
|
||||
url = 'https://api.imgur.com/3/image'
|
||||
headers = {'Authorization': 'Client-ID a10ad04550b0648'}
|
||||
# Upload animation to Imgur and get URL
|
||||
response = requests.post(url, headers=headers, data={"image": base64_data})
|
||||
response = requests.post(url, headers=headers, data={'image': base64_data})
|
||||
result = response.json()
|
||||
await msg.edit_text(result["data"]["link"])
|
||||
await msg.edit_text(result['data']['link'])
|
||||
else:
|
||||
await msg.edit_text(
|
||||
"Please reply to a photo or animation (GIF) to upload to Imgur."
|
||||
)
|
||||
await msg.edit_text('Please reply to a photo or animation (GIF) to upload to Imgur.')
|
||||
|
||||
|
||||
modules_help["imgur"] = {
|
||||
"imgur [img]*": "upload a photo or animation (GIF) to imgur",
|
||||
modules_help['imgur'] = {
|
||||
'imgur [img]*': 'upload a photo or animation (GIF) to imgur',
|
||||
}
|
||||
|
||||
+18
-26
@@ -1,55 +1,47 @@
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.types import Message
|
||||
|
||||
from utils.misc import prefix, modules_help
|
||||
|
||||
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.types import Message
|
||||
from pyrogram.errors import MessageNotModified
|
||||
import os
|
||||
|
||||
import pygments
|
||||
from pygments.formatters import ImageFormatter
|
||||
from pygments.lexers import Python3Lexer
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.errors import MessageNotModified
|
||||
from pyrogram.types import Message
|
||||
from utils.misc import modules_help, prefix
|
||||
|
||||
|
||||
@Client.on_message(filters.command("ncode", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('ncode', prefix) & filters.me)
|
||||
async def coder_print(client, message: Message):
|
||||
if message.reply_to_message:
|
||||
reply_message = message.reply_to_message
|
||||
if reply_message.media:
|
||||
download_path = await client.download_media(reply_message)
|
||||
with open(download_path, "r") as file:
|
||||
with open(download_path) as file:
|
||||
code = file.read()
|
||||
if os.path.exists(download_path):
|
||||
os.remove(download_path)
|
||||
pygments.highlight(
|
||||
f"{code}",
|
||||
f'{code}',
|
||||
Python3Lexer(),
|
||||
ImageFormatter(font_name="DejaVu Sans Mono", line_numbers=True),
|
||||
"result.png",
|
||||
ImageFormatter(font_name='DejaVu Sans Mono', line_numbers=True),
|
||||
'result.png',
|
||||
)
|
||||
try:
|
||||
sent_message = await message.edit_text(
|
||||
"Pasting this code on my page..."
|
||||
)
|
||||
sent_message = await message.edit_text('Pasting this code on my page...')
|
||||
await client.send_document(
|
||||
chat_id=message.chat.id,
|
||||
document="result.png",
|
||||
caption="Code highlighted by Pygments",
|
||||
document='result.png',
|
||||
caption='Code highlighted by Pygments',
|
||||
reply_to_message_id=message.id,
|
||||
)
|
||||
except MessageNotModified:
|
||||
pass
|
||||
await sent_message.delete()
|
||||
if os.path.exists("result.png"):
|
||||
os.remove("result.png")
|
||||
if os.path.exists('result.png'):
|
||||
os.remove('result.png')
|
||||
else:
|
||||
return await message.reply_text("Please reply to a text or a file.")
|
||||
return await message.reply_text('Please reply to a text or a file.')
|
||||
else:
|
||||
return await message.reply_text("Please reply to a text or a file.")
|
||||
return await message.reply_text('Please reply to a text or a file.')
|
||||
|
||||
|
||||
modules_help["ncode"] = {
|
||||
"ncode": "Highlight the code using Pygments and send it as an image."
|
||||
}
|
||||
modules_help['ncode'] = {'ncode': 'Highlight the code using Pygments and send it as an image.'}
|
||||
|
||||
+24
-35
@@ -1,13 +1,14 @@
|
||||
from pyrogram import Client, filters, enums
|
||||
from pyrogram.types import Message, InputMediaPhoto
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
import requests
|
||||
import asyncio
|
||||
from io import BytesIO
|
||||
|
||||
import requests
|
||||
from PIL import Image
|
||||
from pyrogram import Client, enums, filters
|
||||
from pyrogram.types import InputMediaPhoto, Message
|
||||
from utils.misc import modules_help, prefix
|
||||
|
||||
# Pinterest API URL
|
||||
API_URL = "https://bk9.fun/pinterest/search?q="
|
||||
API_URL = 'https://bk9.fun/pinterest/search?q='
|
||||
|
||||
|
||||
def resize_image(image_bytes):
|
||||
@@ -17,13 +18,13 @@ def resize_image(image_bytes):
|
||||
if img.size > max_size:
|
||||
img.thumbnail(max_size)
|
||||
output = BytesIO()
|
||||
img.save(output, format="JPEG")
|
||||
img.save(output, format='JPEG')
|
||||
output.seek(0)
|
||||
return output
|
||||
image_bytes.seek(0) # Reset pointer if not resized
|
||||
return image_bytes
|
||||
except Exception as e:
|
||||
print(f"Error resizing image: {e}")
|
||||
print(f'Error resizing image: {e}')
|
||||
return image_bytes
|
||||
|
||||
|
||||
@@ -34,69 +35,57 @@ async def download_image(url):
|
||||
img_bytes = BytesIO(response.content)
|
||||
return resize_image(img_bytes)
|
||||
except Exception as e:
|
||||
print(f"Error downloading image: {e}")
|
||||
print(f'Error downloading image: {e}')
|
||||
return None
|
||||
|
||||
|
||||
@Client.on_message(filters.command("pinterest", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('pinterest', prefix) & filters.me)
|
||||
async def pinterest_search(client: Client, message: Message):
|
||||
if len(message.command) < 2:
|
||||
await message.edit(
|
||||
"Usage: `pinterest [number] <query>`", parse_mode=enums.ParseMode.MARKDOWN
|
||||
)
|
||||
await message.edit('Usage: `pinterest [number] <query>`', parse_mode=enums.ParseMode.MARKDOWN)
|
||||
return
|
||||
|
||||
num_pics = int(message.command[1]) if message.command[1].isdigit() else 10
|
||||
query = " ".join(message.command[2:])
|
||||
query = ' '.join(message.command[2:])
|
||||
|
||||
# Update status
|
||||
status_message = await message.edit(
|
||||
"Searching for images...", parse_mode=enums.ParseMode.MARKDOWN
|
||||
)
|
||||
status_message = await message.edit('Searching for images...', parse_mode=enums.ParseMode.MARKDOWN)
|
||||
|
||||
url = f"{API_URL}{query}"
|
||||
url = f'{API_URL}{query}'
|
||||
response = requests.get(url)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data.get("status"):
|
||||
urls = [item["images_url"] for item in data.get("BK9", [])[:num_pics]]
|
||||
if data.get('status'):
|
||||
urls = [item['images_url'] for item in data.get('BK9', [])[:num_pics]]
|
||||
images = [download_image(img_url) for img_url in urls]
|
||||
|
||||
# Download images
|
||||
downloaded_images = await asyncio.gather(*images)
|
||||
|
||||
media = [
|
||||
InputMediaPhoto(media=img_bytes)
|
||||
for img_bytes in downloaded_images
|
||||
if img_bytes
|
||||
]
|
||||
media = [InputMediaPhoto(media=img_bytes) for img_bytes in downloaded_images if img_bytes]
|
||||
|
||||
if media:
|
||||
await status_message.edit(
|
||||
"Uploading pictures...", parse_mode=enums.ParseMode.MARKDOWN
|
||||
)
|
||||
await status_message.edit('Uploading pictures...', parse_mode=enums.ParseMode.MARKDOWN)
|
||||
while media:
|
||||
batch = media[:10]
|
||||
media = media[10:]
|
||||
await message.reply_media_group(batch)
|
||||
await status_message.delete() # Delete status message after uploading
|
||||
else:
|
||||
await status_message.edit(
|
||||
"No valid images found.", parse_mode=enums.ParseMode.MARKDOWN
|
||||
)
|
||||
await status_message.edit('No valid images found.', parse_mode=enums.ParseMode.MARKDOWN)
|
||||
else:
|
||||
await status_message.edit(
|
||||
"No images found for the given query.",
|
||||
'No images found for the given query.',
|
||||
parse_mode=enums.ParseMode.MARKDOWN,
|
||||
)
|
||||
else:
|
||||
await status_message.edit(
|
||||
"An error occurred, please try again later.",
|
||||
'An error occurred, please try again later.',
|
||||
parse_mode=enums.ParseMode.MARKDOWN,
|
||||
)
|
||||
|
||||
|
||||
modules_help["pinterest"] = {
|
||||
"pinterest [number]* [query]": "Get images from Pinterest. Default number of images is 10",
|
||||
modules_help['pinterest'] = {
|
||||
'pinterest [number]* [query]': 'Get images from Pinterest. Default number of images is 10',
|
||||
}
|
||||
|
||||
+27
-32
@@ -1,26 +1,27 @@
|
||||
from utils.misc import modules_help, prefix
|
||||
import os
|
||||
|
||||
import requests
|
||||
from modules.url import generate_screenshot
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.types import Message
|
||||
from modules.url import generate_screenshot
|
||||
import os
|
||||
from utils.misc import modules_help, prefix
|
||||
|
||||
# API endpoints for reverse image search engines
|
||||
SEARCH_ENGINES = {
|
||||
"lens": "https://lens.google.com/uploadbyurl?url={image}",
|
||||
"reverse": "https://www.google.com/searchbyimage?sbisrc=4chanx&image_url={image}&safe=off",
|
||||
"tineye": "https://www.tineye.com/search?url={image}",
|
||||
"bing": "https://www.bing.com/images/search?view=detailv2&iss=sbi&form=SBIVSP&sbisrc=UrlPaste&q=imgurl:{image}",
|
||||
"yandex": "https://yandex.com/images/search?source=collections&&url={image}&rpt=imageview",
|
||||
"saucenao": "https://saucenao.com/search.php?db=999&url={image}",
|
||||
'lens': 'https://lens.google.com/uploadbyurl?url={image}',
|
||||
'reverse': 'https://www.google.com/searchbyimage?sbisrc=4chanx&image_url={image}&safe=off',
|
||||
'tineye': 'https://www.tineye.com/search?url={image}',
|
||||
'bing': 'https://www.bing.com/images/search?view=detailv2&iss=sbi&form=SBIVSP&sbisrc=UrlPaste&q=imgurl:{image}',
|
||||
'yandex': 'https://yandex.com/images/search?source=collections&&url={image}&rpt=imageview',
|
||||
'saucenao': 'https://saucenao.com/search.php?db=999&url={image}',
|
||||
}
|
||||
|
||||
|
||||
@Client.on_message(filters.command("risearch", prefix) & filters.reply)
|
||||
@Client.on_message(filters.command('risearch', prefix) & filters.reply)
|
||||
async def reverse_image_search(client: Client, message: Message):
|
||||
if not message.reply_to_message or not message.reply_to_message.photo:
|
||||
await message.reply_text(
|
||||
f"Please reply to an image with <code>{prefix}risearch [engine]</code> or <code>{prefix}risearch</code>."
|
||||
f'Please reply to an image with <code>{prefix}risearch [engine]</code> or <code>{prefix}risearch</code>.'
|
||||
)
|
||||
return
|
||||
|
||||
@@ -31,16 +32,14 @@ async def reverse_image_search(client: Client, message: Message):
|
||||
else list(SEARCH_ENGINES.keys())
|
||||
)
|
||||
|
||||
invalid_engines = [
|
||||
engine for engine in engines_to_use if engine not in SEARCH_ENGINES
|
||||
]
|
||||
invalid_engines = [engine for engine in engines_to_use if engine not in SEARCH_ENGINES]
|
||||
if invalid_engines:
|
||||
await message.reply_text(
|
||||
f"Invalid engine(s): {', '.join(invalid_engines)}. Available: {', '.join(SEARCH_ENGINES.keys())}"
|
||||
f'Invalid engine(s): {", ".join(invalid_engines)}. Available: {", ".join(SEARCH_ENGINES.keys())}'
|
||||
)
|
||||
return
|
||||
|
||||
processing_message = await message.edit_text("Processing the image...")
|
||||
processing_message = await message.edit_text('Processing the image...')
|
||||
|
||||
try:
|
||||
# Download and upload the image
|
||||
@@ -48,7 +47,7 @@ async def reverse_image_search(client: Client, message: Message):
|
||||
img_url = upload_image(photo_path)
|
||||
print(img_url)
|
||||
if not img_url:
|
||||
await processing_message.edit("Error: Could not upload the image.")
|
||||
await processing_message.edit('Error: Could not upload the image.')
|
||||
return
|
||||
|
||||
# Perform searches for the selected engines
|
||||
@@ -56,7 +55,7 @@ async def reverse_image_search(client: Client, message: Message):
|
||||
search_url = SEARCH_ENGINES[engine].format(image=img_url)
|
||||
await send_screenshot(client, message, search_url, engine)
|
||||
except Exception as e:
|
||||
await processing_message.edit(f"An error occurred: {e}")
|
||||
await processing_message.edit(f'An error occurred: {e}')
|
||||
finally:
|
||||
if photo_path and os.path.exists(photo_path):
|
||||
os.remove(photo_path)
|
||||
@@ -65,15 +64,13 @@ async def reverse_image_search(client: Client, message: Message):
|
||||
def upload_image(photo_path):
|
||||
"""Uploads an image to tmpfiles.org and returns the direct download URL."""
|
||||
try:
|
||||
with open(photo_path, "rb") as image_file:
|
||||
response = requests.post(
|
||||
"https://tmpfiles.org/api/v1/upload", files={"file": image_file}
|
||||
)
|
||||
with open(photo_path, 'rb') as image_file:
|
||||
response = requests.post('https://tmpfiles.org/api/v1/upload', files={'file': image_file})
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
url = data["data"]["url"]
|
||||
pic_url = url.split("/")[-2] + "/" + url.split("/")[-1]
|
||||
direct_download_url = url.replace(f"/{pic_url}", f"/dl/{pic_url}")
|
||||
url = data['data']['url']
|
||||
pic_url = url.split('/')[-2] + '/' + url.split('/')[-1]
|
||||
direct_download_url = url.replace(f'/{pic_url}', f'/dl/{pic_url}')
|
||||
print(direct_download_url)
|
||||
return direct_download_url
|
||||
else:
|
||||
@@ -89,17 +86,15 @@ async def send_screenshot(client, message, url, engine_name):
|
||||
await client.send_photo(
|
||||
message.chat.id,
|
||||
screenshot_data,
|
||||
caption=f"<b>{engine_name.capitalize()} Result</b>\nURL: <code>{url}</code>",
|
||||
caption=f'<b>{engine_name.capitalize()} Result</b>\nURL: <code>{url}</code>',
|
||||
reply_to_message_id=message.id,
|
||||
)
|
||||
else:
|
||||
await message.reply(
|
||||
f"Failed to take screenshot for {engine_name.capitalize()}."
|
||||
)
|
||||
await message.reply(f'Failed to take screenshot for {engine_name.capitalize()}.')
|
||||
|
||||
|
||||
# Add module details to help
|
||||
modules_help["risearch"] = {
|
||||
"risearch": f"Reply to a photo with `{prefix}risearch [engine]` (e.g., `{prefix}risearch lens`, `{prefix}risearch bing`) "
|
||||
f"\nor use `{prefix}risearch` to analyze the image with all engines.",
|
||||
modules_help['risearch'] = {
|
||||
'risearch': f'Reply to a photo with `{prefix}risearch [engine]` (e.g., `{prefix}risearch lens`, `{prefix}risearch bing`) '
|
||||
f'\nor use `{prefix}risearch` to analyze the image with all engines.',
|
||||
}
|
||||
|
||||
+36
-44
@@ -1,64 +1,60 @@
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import aiohttp
|
||||
import requests
|
||||
from pyrogram import Client, enums, filters
|
||||
from pyrogram.types import Message
|
||||
import requests
|
||||
from utils.misc import modules_help, prefix
|
||||
|
||||
|
||||
class AioHttp:
|
||||
async def get_json(self, link):
|
||||
headers = {
|
||||
"accept": "*/*",
|
||||
"accept-language": "en-US",
|
||||
"cache-control": "no-cache",
|
||||
"client-geo-region": "global",
|
||||
"dnt": "1",
|
||||
"pragma": "no-cache",
|
||||
"priority": "u=1, i",
|
||||
"sec-ch-ua": '"Chromium";v="130", "Microsoft Edge";v="130", "Not?A_Brand";v="99"',
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": '"Windows"',
|
||||
"sec-fetch-dest": "empty",
|
||||
"sec-fetch-mode": "cors",
|
||||
"sec-fetch-site": "same-origin",
|
||||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0",
|
||||
'accept': '*/*',
|
||||
'accept-language': 'en-US',
|
||||
'cache-control': 'no-cache',
|
||||
'client-geo-region': 'global',
|
||||
'dnt': '1',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'sec-ch-ua': '"Chromium";v="130", "Microsoft Edge";v="130", "Not?A_Brand";v="99"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'same-origin',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0',
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(link, headers=headers) as resp:
|
||||
return await resp.json()
|
||||
async with aiohttp.ClientSession() as session, session.get(link, headers=headers) as resp:
|
||||
return await resp.json()
|
||||
|
||||
|
||||
@Client.on_message(filters.command("unsplash", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('unsplash', prefix) & filters.me)
|
||||
async def unsplash(client: Client, message: Message):
|
||||
if len(message.command) > 1 and isinstance(message.command[1], str):
|
||||
keyword = message.command[1]
|
||||
unsplash_dir = "downloads/unsplash/"
|
||||
unsplash_dir = 'downloads/unsplash/'
|
||||
if not os.path.exists(unsplash_dir):
|
||||
os.makedirs(unsplash_dir)
|
||||
|
||||
if len(message.command) > 2 and 2 <= int(message.command[2]) <= 10:
|
||||
await message.edit(
|
||||
"<b>Getting Pictures</b>", parse_mode=enums.ParseMode.HTML
|
||||
)
|
||||
await message.edit('<b>Getting Pictures</b>', parse_mode=enums.ParseMode.HTML)
|
||||
count = int(message.command[2])
|
||||
images = []
|
||||
data = await AioHttp().get_json(
|
||||
f"https://unsplash.com/napi/search/photos?page=1&per_page={count}&query={keyword}"
|
||||
f'https://unsplash.com/napi/search/photos?page=1&per_page={count}&query={keyword}'
|
||||
)
|
||||
while len(images) < count:
|
||||
for ia in range(len(images), count):
|
||||
img = data["results"][ia]["urls"]["raw"]
|
||||
if img.startswith("https://images.unsplash.com/photo"):
|
||||
img = data['results'][ia]['urls']['raw']
|
||||
if img.startswith('https://images.unsplash.com/photo'):
|
||||
image_content = requests.get(img).content
|
||||
with open(f"{unsplash_dir}/unsplash_{ia}.jpg", "wb") as f:
|
||||
with open(f'{unsplash_dir}/unsplash_{ia}.jpg', 'wb') as f:
|
||||
f.write(image_content)
|
||||
imgr = f"{unsplash_dir}/unsplash_{ia}.jpg"
|
||||
imgr = f'{unsplash_dir}/unsplash_{ia}.jpg'
|
||||
images.append(imgr)
|
||||
else:
|
||||
images.append(img)
|
||||
@@ -72,23 +68,19 @@ async def unsplash(client: Client, message: Message):
|
||||
shutil.rmtree(unsplash_dir)
|
||||
return
|
||||
else:
|
||||
await message.edit(
|
||||
"<b>Getting Picture</b>", parse_mode=enums.ParseMode.HTML
|
||||
)
|
||||
await message.edit('<b>Getting Picture</b>', parse_mode=enums.ParseMode.HTML)
|
||||
data = await AioHttp().get_json(
|
||||
f"https://unsplash.com/napi/search/photos?page=1&per_page=1&query={keyword}"
|
||||
)
|
||||
img = data["results"][0]["urls"]["raw"]
|
||||
await asyncio.gather(
|
||||
message.delete(), client.send_document(message.chat.id, str(img))
|
||||
f'https://unsplash.com/napi/search/photos?page=1&per_page=1&query={keyword}'
|
||||
)
|
||||
img = data['results'][0]['urls']['raw']
|
||||
await asyncio.gather(message.delete(), client.send_document(message.chat.id, str(img)))
|
||||
|
||||
|
||||
modules_help["unsplash"] = {
|
||||
"unsplash": f"[keyword]*",
|
||||
"unsplash": f"[keyword]* [number of results you want]*\n"
|
||||
"Makes a request to <code>unsplash.com</code> and sends the image with the keyword you provided.\n\n"
|
||||
"<b>Note:</b>\n1. The number of results you can get is limited to 10.\n"
|
||||
"2. Keyword is required and should be of one word only!.\n"
|
||||
"3. Images are sent as document to maintain quality.",
|
||||
modules_help['unsplash'] = {
|
||||
'unsplash': '[keyword]*',
|
||||
'unsplash': '[keyword]* [number of results you want]*\n'
|
||||
'Makes a request to <code>unsplash.com</code> and sends the image with the keyword you provided.\n\n'
|
||||
'<b>Note:</b>\n1. The number of results you can get is limited to 10.\n'
|
||||
'2. Keyword is required and should be of one word only!.\n'
|
||||
'3. Images are sent as document to maintain quality.',
|
||||
}
|
||||
|
||||
+25
-36
@@ -1,12 +1,13 @@
|
||||
from pyrogram import Client, filters, enums
|
||||
from pyrogram.types import Message, InputMediaPhoto
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
import requests
|
||||
import asyncio
|
||||
from io import BytesIO
|
||||
|
||||
import requests
|
||||
from PIL import Image
|
||||
from pyrogram import Client, enums, filters
|
||||
from pyrogram.types import InputMediaPhoto, Message
|
||||
from utils.misc import modules_help, prefix
|
||||
|
||||
API_URL = "https://bk9.fun/search/unsplash?q="
|
||||
API_URL = 'https://bk9.fun/search/unsplash?q='
|
||||
|
||||
|
||||
def resize_image(image_bytes):
|
||||
@@ -16,13 +17,13 @@ def resize_image(image_bytes):
|
||||
if img.size > max_size:
|
||||
img.thumbnail(max_size)
|
||||
output = BytesIO()
|
||||
img.save(output, format="JPEG")
|
||||
img.save(output, format='JPEG')
|
||||
output.seek(0)
|
||||
return output
|
||||
image_bytes.seek(0) # Reset pointer if not resized
|
||||
return image_bytes
|
||||
except Exception as e:
|
||||
print(f"Error resizing image: {e}")
|
||||
print(f'Error resizing image: {e}')
|
||||
return image_bytes
|
||||
|
||||
|
||||
@@ -34,70 +35,58 @@ async def download_image(url):
|
||||
resized_img_bytes = resize_image(img_bytes)
|
||||
return resized_img_bytes
|
||||
except Exception as e:
|
||||
print(f"Error downloading image: {e}")
|
||||
print(f'Error downloading image: {e}')
|
||||
return None
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["unsplash2", "usp2"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['unsplash2', 'usp2'], prefix) & filters.me)
|
||||
async def imgsearch(client: Client, message: Message):
|
||||
if len(message.command) < 2:
|
||||
await message.edit(
|
||||
"Usage: `img [number] <query>`", parse_mode=enums.ParseMode.MARKDOWN
|
||||
)
|
||||
await message.edit('Usage: `img [number] <query>`', parse_mode=enums.ParseMode.MARKDOWN)
|
||||
return
|
||||
|
||||
num_pics = int(message.command[1]) if message.command[1].isdigit() else 10
|
||||
query = " ".join(message.command[2:])
|
||||
query = ' '.join(message.command[2:])
|
||||
|
||||
# Update status
|
||||
status_message = await message.edit(
|
||||
"Searching for images...", parse_mode=enums.ParseMode.MARKDOWN
|
||||
)
|
||||
status_message = await message.edit('Searching for images...', parse_mode=enums.ParseMode.MARKDOWN)
|
||||
|
||||
url = f"{API_URL}{query}"
|
||||
url = f'{API_URL}{query}'
|
||||
response = requests.get(url)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data.get("status"):
|
||||
urls = data.get("BK9", [])[:num_pics]
|
||||
if data.get('status'):
|
||||
urls = data.get('BK9', [])[:num_pics]
|
||||
images = [download_image(img_url) for img_url in urls]
|
||||
|
||||
# Download images
|
||||
downloaded_images = await asyncio.gather(*images)
|
||||
|
||||
media = [
|
||||
InputMediaPhoto(media=img_bytes)
|
||||
for img_bytes in downloaded_images
|
||||
if img_bytes
|
||||
]
|
||||
media = [InputMediaPhoto(media=img_bytes) for img_bytes in downloaded_images if img_bytes]
|
||||
|
||||
if media:
|
||||
await status_message.edit(
|
||||
"Uploading pictures...", parse_mode=enums.ParseMode.MARKDOWN
|
||||
)
|
||||
await status_message.edit('Uploading pictures...', parse_mode=enums.ParseMode.MARKDOWN)
|
||||
while media:
|
||||
batch = media[:10]
|
||||
media = media[10:]
|
||||
await message.reply_media_group(batch)
|
||||
await status_message.delete() # Delete status message after uploading
|
||||
else:
|
||||
await status_message.edit(
|
||||
"No valid images found.", parse_mode=enums.ParseMode.MARKDOWN
|
||||
)
|
||||
await status_message.edit('No valid images found.', parse_mode=enums.ParseMode.MARKDOWN)
|
||||
else:
|
||||
await status_message.edit(
|
||||
"No images found for the given query.",
|
||||
'No images found for the given query.',
|
||||
parse_mode=enums.ParseMode.MARKDOWN,
|
||||
)
|
||||
else:
|
||||
await status_message.edit(
|
||||
"An error occurred, please try again later.",
|
||||
'An error occurred, please try again later.',
|
||||
parse_mode=enums.ParseMode.MARKDOWN,
|
||||
)
|
||||
|
||||
|
||||
modules_help["unsplash2"] = {
|
||||
"unsplash2 [number]* [query]": "Get HD images. Default number of images is 10",
|
||||
"usp2 [number]* [query]": "Get HD images. Default number of images is 10",
|
||||
modules_help['unsplash2'] = {
|
||||
'unsplash2 [number]* [query]': 'Get HD images. Default number of images is 10',
|
||||
'usp2 [number]* [query]': 'Get HD images. Default number of images is 10',
|
||||
}
|
||||
|
||||
+24
-24
@@ -1,23 +1,23 @@
|
||||
from datetime import datetime
|
||||
import sys
|
||||
from pyrogram import Client
|
||||
from datetime import datetime
|
||||
|
||||
from pyrogram import Client
|
||||
from utils import config
|
||||
|
||||
common_params = {
|
||||
"api_id": config.api_id,
|
||||
"api_hash": config.api_hash,
|
||||
"hide_password": True,
|
||||
"test_mode": config.test_server,
|
||||
'api_id': config.api_id,
|
||||
'api_hash': config.api_hash,
|
||||
'hide_password': True,
|
||||
'test_mode': config.test_server,
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
if config.STRINGSESSION:
|
||||
common_params["session_string"] = config.STRINGSESSION
|
||||
common_params['session_string'] = config.STRINGSESSION
|
||||
|
||||
app = Client("my_account", **common_params)
|
||||
app = Client('my_account', **common_params)
|
||||
|
||||
if config.db_type in ["mongo", "mongodb"]:
|
||||
if config.db_type in ['mongo', 'mongodb']:
|
||||
from pymongo import MongoClient, errors
|
||||
|
||||
db = MongoClient(config.db_url)
|
||||
@@ -26,27 +26,27 @@ if __name__ == "__main__":
|
||||
except errors.ConnectionFailure as e:
|
||||
raise RuntimeError(
|
||||
"MongoDB server isn't available! "
|
||||
f"Provided url: {config.db_url}. "
|
||||
"Enter valid URL and restart installation"
|
||||
f'Provided url: {config.db_url}. '
|
||||
'Enter valid URL and restart installation'
|
||||
) from e
|
||||
|
||||
install_type = sys.argv[1] if len(sys.argv) > 1 else "3"
|
||||
if install_type == "1":
|
||||
restart = "pm2 restart Moon"
|
||||
elif install_type == "2":
|
||||
restart = "sudo systemctl restart Moon"
|
||||
install_type = sys.argv[1] if len(sys.argv) > 1 else '3'
|
||||
if install_type == '1':
|
||||
restart = 'pm2 restart Moon'
|
||||
elif install_type == '2':
|
||||
restart = 'sudo systemctl restart Moon'
|
||||
else:
|
||||
restart = "cd Moon-Userbot/ && python main.py"
|
||||
restart = 'cd Moon-Userbot/ && python main.py'
|
||||
|
||||
app.start()
|
||||
try:
|
||||
app.send_message(
|
||||
"me",
|
||||
f"<b>[{datetime.now()}] Userbot launched! \n"
|
||||
"Custom modules: @moonub_modules\n"
|
||||
f"For restart, enter:</b>\n"
|
||||
f"<code>{restart}</code>",
|
||||
'me',
|
||||
f'<b>[{datetime.now()}] Userbot launched! \n'
|
||||
'Custom modules: @moonub_modules\n'
|
||||
f'For restart, enter:</b>\n'
|
||||
f'<code>{restart}</code>',
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"[ERROR]: Sending Message to me failed! {e}")
|
||||
print(f'[ERROR]: Sending Message to me failed! {e}')
|
||||
app.stop()
|
||||
|
||||
+51
-60
@@ -39,92 +39,88 @@
|
||||
# "pySmartDL",
|
||||
# ]
|
||||
# ///
|
||||
import os
|
||||
import logging
|
||||
|
||||
import sqlite3
|
||||
import os
|
||||
import platform
|
||||
import sqlite3
|
||||
import subprocess
|
||||
|
||||
from pyrogram import Client, idle, errors
|
||||
from pyrogram.enums.parse_mode import ParseMode
|
||||
from pyrogram.raw.functions.account import GetAuthorizations, DeleteAccount
|
||||
import requests
|
||||
|
||||
from pyrogram import Client, errors, idle
|
||||
from pyrogram.enums.parse_mode import ParseMode
|
||||
from pyrogram.raw.functions.account import DeleteAccount, GetAuthorizations
|
||||
from utils import config
|
||||
from utils.db import db
|
||||
from utils.misc import gitrepo, userbot_version
|
||||
from utils.scripts import restart
|
||||
from utils.rentry import rentry_cleanup_job
|
||||
from utils.misc import userbot_version
|
||||
from utils.module import ModuleManager
|
||||
from utils.rentry import rentry_cleanup_job
|
||||
from utils.scripts import restart
|
||||
|
||||
SCRIPT_PATH = os.path.dirname(os.path.realpath(__file__))
|
||||
if SCRIPT_PATH != os.getcwd():
|
||||
if os.getcwd() != SCRIPT_PATH:
|
||||
os.chdir(SCRIPT_PATH)
|
||||
|
||||
common_params = {
|
||||
"api_id": config.api_id,
|
||||
"api_hash": config.api_hash,
|
||||
"hide_password": True,
|
||||
"workdir": SCRIPT_PATH,
|
||||
"app_version": userbot_version,
|
||||
"device_model": f"mUserbot",
|
||||
"system_version": platform.version() + " " + platform.machine(),
|
||||
"sleep_threshold": 30,
|
||||
"test_mode": config.test_server,
|
||||
"parse_mode": ParseMode.HTML,
|
||||
'api_id': config.api_id,
|
||||
'api_hash': config.api_hash,
|
||||
'hide_password': True,
|
||||
'workdir': SCRIPT_PATH,
|
||||
'app_version': userbot_version,
|
||||
'device_model': 'mUserbot',
|
||||
'system_version': platform.version() + ' ' + platform.machine(),
|
||||
'sleep_threshold': 30,
|
||||
'test_mode': config.test_server,
|
||||
'parse_mode': ParseMode.HTML,
|
||||
}
|
||||
|
||||
if config.STRINGSESSION:
|
||||
common_params["session_string"] = config.STRINGSESSION
|
||||
common_params['session_string'] = config.STRINGSESSION
|
||||
|
||||
app = Client("my_account", **common_params)
|
||||
app = Client('my_account', **common_params)
|
||||
|
||||
|
||||
def load_missing_modules():
|
||||
all_modules = db.get("custom.modules", "allModules", [])
|
||||
all_modules = db.get('custom.modules', 'allModules', [])
|
||||
if not all_modules:
|
||||
return
|
||||
|
||||
custom_modules_path = f"{SCRIPT_PATH}/modules/custom_modules"
|
||||
custom_modules_path = f'{SCRIPT_PATH}/modules/custom_modules'
|
||||
os.makedirs(custom_modules_path, exist_ok=True)
|
||||
|
||||
try:
|
||||
resp = requests.get(
|
||||
"https://raw.githubusercontent.com/The-MoonTg-project/custom_modules/main/full.txt",
|
||||
'https://raw.githubusercontent.com/The-MoonTg-project/custom_modules/main/full.txt',
|
||||
timeout=10,
|
||||
)
|
||||
if not resp.ok:
|
||||
logging.error(
|
||||
"Failed to fetch custom modules list: HTTP %s",
|
||||
'Failed to fetch custom modules list: HTTP %s',
|
||||
resp.status_code,
|
||||
)
|
||||
return
|
||||
f = resp.text
|
||||
except Exception as e:
|
||||
logging.error("Failed to fetch custom modules list: %s", e)
|
||||
logging.error('Failed to fetch custom modules list: %s', e)
|
||||
return
|
||||
modules_dict = {
|
||||
line.split("/")[-1].split()[0]: line.strip() for line in f.splitlines()
|
||||
}
|
||||
modules_dict = {line.split('/')[-1].split()[0]: line.strip() for line in f.splitlines()}
|
||||
|
||||
for module_name in all_modules:
|
||||
module_path = f"{custom_modules_path}/{module_name}.py"
|
||||
module_path = f'{custom_modules_path}/{module_name}.py'
|
||||
if not os.path.exists(module_path) and module_name in modules_dict:
|
||||
url = f"https://raw.githubusercontent.com/The-MoonTg-project/custom_modules/main/{modules_dict[module_name]}.py"
|
||||
url = f'https://raw.githubusercontent.com/The-MoonTg-project/custom_modules/main/{modules_dict[module_name]}.py'
|
||||
resp = requests.get(url)
|
||||
if resp.ok:
|
||||
with open(module_path, "wb") as f:
|
||||
with open(module_path, 'wb') as f:
|
||||
f.write(resp.content)
|
||||
logging.info("Loaded missing module: %s", module_name)
|
||||
logging.info('Loaded missing module: %s', module_name)
|
||||
else:
|
||||
logging.warning("Failed to load module: %s", module_name)
|
||||
logging.warning('Failed to load module: %s', module_name)
|
||||
|
||||
|
||||
async def main():
|
||||
logging.basicConfig(
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
handlers=[logging.FileHandler("moonlogs.txt"), logging.StreamHandler()],
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[logging.FileHandler('moonlogs.txt'), logging.StreamHandler()],
|
||||
level=logging.INFO,
|
||||
)
|
||||
DeleteAccount.__new__ = None
|
||||
@@ -132,49 +128,44 @@ async def main():
|
||||
try:
|
||||
await app.start()
|
||||
except sqlite3.OperationalError as e:
|
||||
if str(e) == "database is locked" and os.name == "posix":
|
||||
logging.warning(
|
||||
"Session file is locked. Trying to kill blocking process..."
|
||||
)
|
||||
subprocess.run(["fuser", "-k", "my_account.session"], check=True)
|
||||
if str(e) == 'database is locked' and os.name == 'posix':
|
||||
logging.warning('Session file is locked. Trying to kill blocking process...')
|
||||
subprocess.run(['fuser', '-k', 'my_account.session'], check=True)
|
||||
restart()
|
||||
raise
|
||||
except (errors.NotAcceptable, errors.Unauthorized) as e:
|
||||
logging.error(
|
||||
"%s: %s\nMoving session file to my_account.session-old...",
|
||||
'%s: %s\nMoving session file to my_account.session-old...',
|
||||
e.__class__.__name__,
|
||||
e,
|
||||
)
|
||||
os.rename("./my_account.session", "./my_account.session-old")
|
||||
os.rename('./my_account.session', './my_account.session-old')
|
||||
restart()
|
||||
|
||||
load_missing_modules()
|
||||
module_manager = ModuleManager.get_instance()
|
||||
await module_manager.load_modules(app)
|
||||
|
||||
if info := db.get("core.updater", "restart_info"):
|
||||
if info := db.get('core.updater', 'restart_info'):
|
||||
text = {
|
||||
"restart": "<b>Restart completed!</b>",
|
||||
"update": "<b>Update process completed!</b>",
|
||||
}[info["type"]]
|
||||
'restart': '<b>Restart completed!</b>',
|
||||
'update': '<b>Update process completed!</b>',
|
||||
}[info['type']]
|
||||
try:
|
||||
await app.edit_message_text(info["chat_id"], info["message_id"], text)
|
||||
await app.edit_message_text(info['chat_id'], info['message_id'], text)
|
||||
except errors.RPCError:
|
||||
pass
|
||||
db.remove("core.updater", "restart_info")
|
||||
db.remove('core.updater', 'restart_info')
|
||||
|
||||
# required for sessionkiller module
|
||||
if db.get("core.sessionkiller", "enabled", False):
|
||||
if db.get('core.sessionkiller', 'enabled', False):
|
||||
db.set(
|
||||
"core.sessionkiller",
|
||||
"auths_hashes",
|
||||
[
|
||||
auth.hash
|
||||
for auth in (await app.invoke(GetAuthorizations())).authorizations
|
||||
],
|
||||
'core.sessionkiller',
|
||||
'auths_hashes',
|
||||
[auth.hash for auth in (await app.invoke(GetAuthorizations())).authorizations],
|
||||
)
|
||||
|
||||
logging.info("Moon-Userbot started!")
|
||||
logging.info('Moon-Userbot started!')
|
||||
|
||||
app.loop.create_task(rentry_cleanup_job())
|
||||
|
||||
@@ -183,5 +174,5 @@ async def main():
|
||||
await app.stop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
app.run(main())
|
||||
|
||||
@@ -2,20 +2,16 @@ from asyncio import sleep
|
||||
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.types import Message
|
||||
|
||||
from utils.misc import modules_help, prefix
|
||||
|
||||
digits = {
|
||||
str(i): el
|
||||
for i, el in enumerate(["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"])
|
||||
}
|
||||
digits = {str(i): el for i, el in enumerate(['0️⃣', '1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '6️⃣', '7️⃣', '8️⃣', '9️⃣'])}
|
||||
|
||||
|
||||
def prettify(val: int) -> str:
|
||||
return "".join(digits[i] for i in str(val))
|
||||
return ''.join(digits[i] for i in str(val))
|
||||
|
||||
|
||||
@Client.on_message(filters.command("ghoul", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('ghoul', prefix) & filters.me)
|
||||
async def ghoul_counter(_, message: Message):
|
||||
await message.delete()
|
||||
|
||||
@@ -33,9 +29,7 @@ async def ghoul_counter(_, message: Message):
|
||||
await msg.edit(prettify(counter))
|
||||
await sleep(1)
|
||||
|
||||
await msg.edit("<b>🤡 GHOUL 🤡</b>")
|
||||
await msg.edit('<b>🤡 GHOUL 🤡</b>')
|
||||
|
||||
|
||||
modules_help["1000-7"] = {
|
||||
"ghoul [count_from]": "counting from 1000 (or given [count_from] to 0 as a ghoul"
|
||||
}
|
||||
modules_help['1000-7'] = {'ghoul [count_from]': 'counting from 1000 (or given [count_from] to 0 as a ghoul'}
|
||||
|
||||
+93
-104
@@ -16,60 +16,57 @@
|
||||
|
||||
from contextlib import suppress
|
||||
|
||||
from pyrogram.enums import ChatType
|
||||
from pyrogram import Client, ContinuePropagation, filters
|
||||
from pyrogram.enums import ChatType
|
||||
from pyrogram.errors import (
|
||||
UserAdminInvalid,
|
||||
ChatAdminRequired,
|
||||
RPCError,
|
||||
UserAdminInvalid,
|
||||
)
|
||||
from pyrogram.raw import functions
|
||||
from pyrogram.types import Message, ChatPermissions
|
||||
|
||||
from pyrogram.types import ChatPermissions, Message
|
||||
from utils.db import db
|
||||
from utils.scripts import format_exc, with_reply
|
||||
from utils.misc import modules_help, prefix
|
||||
|
||||
from utils.handlers import (
|
||||
BanHandler,
|
||||
UnbanHandler,
|
||||
KickHandler,
|
||||
KickDeletedAccountsHandler,
|
||||
TimeMuteHandler,
|
||||
TimeUnmuteHandler,
|
||||
TimeMuteUsersHandler,
|
||||
UnmuteHandler,
|
||||
MuteHandler,
|
||||
DemoteHandler,
|
||||
PromoteHandler,
|
||||
AntiChannelsHandler,
|
||||
DeleteHistoryHandler,
|
||||
AntiRaidHandler,
|
||||
BanHandler,
|
||||
DeleteHistoryHandler,
|
||||
DemoteHandler,
|
||||
KickDeletedAccountsHandler,
|
||||
KickHandler,
|
||||
MuteHandler,
|
||||
PromoteHandler,
|
||||
TimeMuteHandler,
|
||||
TimeMuteUsersHandler,
|
||||
TimeUnmuteHandler,
|
||||
UnbanHandler,
|
||||
UnmuteHandler,
|
||||
)
|
||||
from utils.misc import modules_help, prefix
|
||||
from utils.scripts import format_exc, with_reply
|
||||
|
||||
|
||||
db_cache: dict = db.get_collection("core.ats")
|
||||
db_cache: dict = db.get_collection('core.ats')
|
||||
|
||||
|
||||
def update_cache():
|
||||
db_cache.clear()
|
||||
db_cache.update(db.get_collection("core.ats"))
|
||||
db_cache.update(db.get_collection('core.ats'))
|
||||
|
||||
|
||||
@Client.on_message(filters.group & ~filters.me)
|
||||
async def admintool_handler(_, message: Message):
|
||||
if message.sender_chat and (
|
||||
message.sender_chat.type == "supergroup"
|
||||
or message.sender_chat.id == db_cache.get(f"linked{message.chat.id}", 0)
|
||||
message.sender_chat.type == 'supergroup'
|
||||
or message.sender_chat.id == db_cache.get(f'linked{message.chat.id}', 0)
|
||||
):
|
||||
raise ContinuePropagation
|
||||
|
||||
if message.sender_chat and db_cache.get(f"antich{message.chat.id}", False):
|
||||
if message.sender_chat and db_cache.get(f'antich{message.chat.id}', False):
|
||||
with suppress(RPCError):
|
||||
await message.delete()
|
||||
await message.chat.ban_member(message.sender_chat.id)
|
||||
|
||||
tmuted_users = db_cache.get(f"c{message.chat.id}", [])
|
||||
tmuted_users = db_cache.get(f'c{message.chat.id}', [])
|
||||
if (
|
||||
message.from_user
|
||||
and message.from_user.id in tmuted_users
|
||||
@@ -79,7 +76,7 @@ async def admintool_handler(_, message: Message):
|
||||
with suppress(RPCError):
|
||||
await message.delete()
|
||||
|
||||
if db_cache.get(f"antiraid{message.chat.id}", False):
|
||||
if db_cache.get(f'antiraid{message.chat.id}', False):
|
||||
with suppress(RPCError):
|
||||
await message.delete()
|
||||
if message.from_user:
|
||||
@@ -87,11 +84,9 @@ async def admintool_handler(_, message: Message):
|
||||
elif message.sender_chat:
|
||||
await message.chat.ban_member(message.sender_chat.id)
|
||||
|
||||
if message.new_chat_members and db_cache.get(
|
||||
f"welcome_enabled{message.chat.id}", False
|
||||
):
|
||||
if message.new_chat_members and db_cache.get(f'welcome_enabled{message.chat.id}', False):
|
||||
await message.reply(
|
||||
db_cache.get(f"welcome_text{message.chat.id}"),
|
||||
db_cache.get(f'welcome_text{message.chat.id}'),
|
||||
disable_web_page_preview=True,
|
||||
)
|
||||
|
||||
@@ -111,88 +106,88 @@ async def get_user_and_name(message):
|
||||
)
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["ban"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['ban'], prefix) & filters.me)
|
||||
async def ban_command(client: Client, message: Message):
|
||||
handler = BanHandler(client, message)
|
||||
await handler.handle_ban()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["unban"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['unban'], prefix) & filters.me)
|
||||
async def unban_command(client: Client, message: Message):
|
||||
handler = UnbanHandler(client, message)
|
||||
await handler.handle_unban()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["kick"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['kick'], prefix) & filters.me)
|
||||
async def kick_command(client: Client, message: Message):
|
||||
handler = KickHandler(client, message)
|
||||
await handler.handle_kick()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["kickdel"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['kickdel'], prefix) & filters.me)
|
||||
async def kickdel_cmd(client: Client, message: Message):
|
||||
handler = KickDeletedAccountsHandler(client, message)
|
||||
await handler.kick_deleted_accounts()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["tmute"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['tmute'], prefix) & filters.me)
|
||||
async def tmute_command(client: Client, message: Message):
|
||||
handler = TimeMuteHandler(client, message)
|
||||
await handler.handle_tmute()
|
||||
update_cache()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["tunmute"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['tunmute'], prefix) & filters.me)
|
||||
async def tunmute_command(client: Client, message: Message):
|
||||
handler = TimeUnmuteHandler(client, message)
|
||||
await handler.handle_tunmute()
|
||||
update_cache()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["tmute_users"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['tmute_users'], prefix) & filters.me)
|
||||
async def tunmute_users_command(client: Client, message: Message):
|
||||
handler = TimeMuteUsersHandler(client, message)
|
||||
await handler.list_tmuted_users()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["unmute"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['unmute'], prefix) & filters.me)
|
||||
async def unmute_command(client: Client, message: Message):
|
||||
handler = UnmuteHandler(client, message)
|
||||
await handler.handle_unmute()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["mute"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['mute'], prefix) & filters.me)
|
||||
async def mute_command(client: Client, message: Message):
|
||||
handler = MuteHandler(client, message)
|
||||
await handler.handle_mute()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["demote"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['demote'], prefix) & filters.me)
|
||||
async def demote_command(client: Client, message: Message):
|
||||
handler = DemoteHandler(client, message)
|
||||
await handler.handle_demote()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["promote"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['promote'], prefix) & filters.me)
|
||||
async def promote_command(client: Client, message: Message):
|
||||
handler = PromoteHandler(client, message)
|
||||
await handler.handle_promote()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["antich"], prefix))
|
||||
@Client.on_message(filters.command(['antich'], prefix))
|
||||
async def anti_channels(client: Client, message: Message):
|
||||
handler = AntiChannelsHandler(client, message)
|
||||
await handler.handle_anti_channels()
|
||||
update_cache()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["delete_history", "dh"], prefix))
|
||||
@Client.on_message(filters.command(['delete_history', 'dh'], prefix))
|
||||
async def delete_history(client: Client, message: Message):
|
||||
handler = DeleteHistoryHandler(client, message)
|
||||
await handler.handle_delete_history()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["report_spam", "rs"], prefix))
|
||||
@Client.on_message(filters.command(['report_spam', 'rs'], prefix))
|
||||
@with_reply
|
||||
async def report_spam(client: Client, message: Message):
|
||||
try:
|
||||
@@ -210,33 +205,33 @@ async def report_spam(client: Client, message: Message):
|
||||
except Exception as e:
|
||||
await message.edit(format_exc(e))
|
||||
else:
|
||||
await message.edit(f"<b>Message</a> from {name} was reported</b>")
|
||||
await message.edit(f'<b>Message</a> from {name} was reported</b>')
|
||||
|
||||
|
||||
@Client.on_message(filters.command("pin", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('pin', prefix) & filters.me)
|
||||
@with_reply
|
||||
async def pin(_, message: Message):
|
||||
try:
|
||||
await message.reply_to_message.pin()
|
||||
await message.edit("<b>Pinned!</b>")
|
||||
await message.edit('<b>Pinned!</b>')
|
||||
except Exception as e:
|
||||
await message.edit(format_exc(e))
|
||||
|
||||
|
||||
@Client.on_message(filters.command("unpin", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('unpin', prefix) & filters.me)
|
||||
@with_reply
|
||||
async def unpin(_, message: Message):
|
||||
try:
|
||||
await message.reply_to_message.unpin()
|
||||
await message.edit("<b>Unpinned!</b>")
|
||||
await message.edit('<b>Unpinned!</b>')
|
||||
except Exception as e:
|
||||
await message.edit(format_exc(e))
|
||||
|
||||
|
||||
@Client.on_message(filters.command("ro", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('ro', prefix) & filters.me)
|
||||
async def ro(client: Client, message: Message):
|
||||
if message.chat.type not in [ChatType.GROUP, ChatType.SUPERGROUP]:
|
||||
await message.edit("<b>Invalid chat type</b>")
|
||||
await message.edit('<b>Invalid chat type</b>')
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -250,42 +245,39 @@ async def ro(client: Client, message: Message):
|
||||
perms.can_invite_users,
|
||||
perms.can_pin_messages,
|
||||
]
|
||||
db.set("core.ats", f"ro{message.chat.id}", perms_list)
|
||||
db.set('core.ats', f'ro{message.chat.id}', perms_list)
|
||||
|
||||
try:
|
||||
await client.set_chat_permissions(message.chat.id, ChatPermissions())
|
||||
except (UserAdminInvalid, ChatAdminRequired):
|
||||
await message.edit("<b>No rights</b>")
|
||||
await message.edit('<b>No rights</b>')
|
||||
else:
|
||||
await message.edit(
|
||||
"<b>Read-only mode activated!\n"
|
||||
f"Turn off with:</b><code>{prefix}unro</code>"
|
||||
)
|
||||
await message.edit(f'<b>Read-only mode activated!\nTurn off with:</b><code>{prefix}unro</code>')
|
||||
except Exception as e:
|
||||
await message.edit(format_exc(e))
|
||||
|
||||
|
||||
@Client.on_message(filters.command("unro", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('unro', prefix) & filters.me)
|
||||
async def unro(client: Client, message: Message):
|
||||
if message.chat.type not in [ChatType.GROUP, ChatType.SUPERGROUP]:
|
||||
await message.edit("<b>Invalid chat type</b>")
|
||||
await message.edit('<b>Invalid chat type</b>')
|
||||
return
|
||||
|
||||
try:
|
||||
perms_list = db.get(
|
||||
"core.ats",
|
||||
f"ro{message.chat.id}",
|
||||
'core.ats',
|
||||
f'ro{message.chat.id}',
|
||||
[True, True, False, False, False, False, False],
|
||||
)
|
||||
|
||||
common_perms = {
|
||||
"can_send_messages": perms_list[0],
|
||||
"can_send_media_messages": perms_list[1],
|
||||
"can_send_polls": perms_list[2],
|
||||
"can_add_web_page_previews": perms_list[3],
|
||||
"can_change_info": perms_list[4],
|
||||
"can_invite_users": perms_list[5],
|
||||
"can_pin_messages": perms_list[6],
|
||||
'can_send_messages': perms_list[0],
|
||||
'can_send_media_messages': perms_list[1],
|
||||
'can_send_polls': perms_list[2],
|
||||
'can_add_web_page_previews': perms_list[3],
|
||||
'can_change_info': perms_list[4],
|
||||
'can_invite_users': perms_list[5],
|
||||
'can_pin_messages': perms_list[6],
|
||||
}
|
||||
|
||||
perms = ChatPermissions(**common_perms)
|
||||
@@ -293,61 +285,58 @@ async def unro(client: Client, message: Message):
|
||||
try:
|
||||
await client.set_chat_permissions(message.chat.id, perms)
|
||||
except (UserAdminInvalid, ChatAdminRequired):
|
||||
await message.edit("<b>No rights</b>")
|
||||
await message.edit('<b>No rights</b>')
|
||||
else:
|
||||
await message.edit("<b>Read-only mode disabled!</b>")
|
||||
await message.edit('<b>Read-only mode disabled!</b>')
|
||||
except Exception as e:
|
||||
await message.edit(format_exc(e))
|
||||
|
||||
|
||||
@Client.on_message(filters.command("antiraid", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('antiraid', prefix) & filters.me)
|
||||
async def antiraid(client: Client, message: Message):
|
||||
handler = AntiRaidHandler(client, message)
|
||||
await handler.handle_antiraid()
|
||||
update_cache()
|
||||
|
||||
|
||||
@Client.on_message(filters.command(["welcome", "wc"], prefix) & filters.me)
|
||||
@Client.on_message(filters.command(['welcome', 'wc'], prefix) & filters.me)
|
||||
async def welcome(_, message: Message):
|
||||
if message.chat.type not in [ChatType.GROUP, ChatType.SUPERGROUP]:
|
||||
return await message.edit("<b>Unsupported chat type</b>")
|
||||
return await message.edit('<b>Unsupported chat type</b>')
|
||||
|
||||
if len(message.command) > 1:
|
||||
text = message.text.split(maxsplit=1)[1]
|
||||
db.set("core.ats", f"welcome_enabled{message.chat.id}", True)
|
||||
db.set("core.ats", f"welcome_text{message.chat.id}", text)
|
||||
db.set('core.ats', f'welcome_enabled{message.chat.id}', True)
|
||||
db.set('core.ats', f'welcome_text{message.chat.id}', text)
|
||||
|
||||
await message.edit(
|
||||
f"<b>Welcome enabled in this chat\nText:</b> <code>{text}</code>"
|
||||
)
|
||||
await message.edit(f'<b>Welcome enabled in this chat\nText:</b> <code>{text}</code>')
|
||||
else:
|
||||
db.set("core.ats", f"welcome_enabled{message.chat.id}", False)
|
||||
await message.edit("<b>Welcome disabled in this chat</b>")
|
||||
db.set('core.ats', f'welcome_enabled{message.chat.id}', False)
|
||||
await message.edit('<b>Welcome disabled in this chat</b>')
|
||||
|
||||
update_cache()
|
||||
|
||||
|
||||
modules_help["admintool"] = {
|
||||
"ban [reply]/[username/id]* [reason] [report_spam] [delete_history]": "ban user in chat",
|
||||
"unban [reply]/[username/id]* [reason]": "unban user in chat",
|
||||
"kick [reply]/[userid]* [reason] [report_spam] [delete_history]": "kick user out of chat",
|
||||
"mute [reply]/[userid]* [reason] [1m]/[1h]/[1d]/[1w]": "mute user in chat",
|
||||
"unmute [reply]/[userid]* [reason]": "unmute user in chat",
|
||||
"promote [reply]/[userid]* [prefix]": "promote user in chat",
|
||||
"demote [reply]/[userid]* [reason]": "demote user in chat",
|
||||
"tmute [reply]/[username/id]* [reason]": "delete all new messages from user in chat",
|
||||
"tunmute [reply]/[username/id]* [reason]": "stop deleting all messages from user in chat",
|
||||
"tmute_users": "list of tmuted (.tmute) users",
|
||||
"antich [enable/disable]": "turn on/off blocking channels in this chat",
|
||||
"delete_history [reply]/[username/id]* [reason]": "delete history from member in chat",
|
||||
"report_spam [reply]*": "report spam message in chat",
|
||||
"pin [reply]*": "Pin replied message",
|
||||
"unpin [reply]*": "Unpin replied message",
|
||||
"ro": "enable read-only mode",
|
||||
"unro": "disable read-only mode",
|
||||
"antiraid [on|off]": "when enabled, anyone who writes message will be blocked. Useful in raids. "
|
||||
"Running without arguments equals to toggling state",
|
||||
"welcome [text]*": "enable auto-welcome to new users in groups. "
|
||||
"Running without text equals to disable",
|
||||
"kickdel": "Kick all deleted accounts",
|
||||
modules_help['admintool'] = {
|
||||
'ban [reply]/[username/id]* [reason] [report_spam] [delete_history]': 'ban user in chat',
|
||||
'unban [reply]/[username/id]* [reason]': 'unban user in chat',
|
||||
'kick [reply]/[userid]* [reason] [report_spam] [delete_history]': 'kick user out of chat',
|
||||
'mute [reply]/[userid]* [reason] [1m]/[1h]/[1d]/[1w]': 'mute user in chat',
|
||||
'unmute [reply]/[userid]* [reason]': 'unmute user in chat',
|
||||
'promote [reply]/[userid]* [prefix]': 'promote user in chat',
|
||||
'demote [reply]/[userid]* [reason]': 'demote user in chat',
|
||||
'tmute [reply]/[username/id]* [reason]': 'delete all new messages from user in chat',
|
||||
'tunmute [reply]/[username/id]* [reason]': 'stop deleting all messages from user in chat',
|
||||
'tmute_users': 'list of tmuted (.tmute) users',
|
||||
'antich [enable/disable]': 'turn on/off blocking channels in this chat',
|
||||
'delete_history [reply]/[username/id]* [reason]': 'delete history from member in chat',
|
||||
'report_spam [reply]*': 'report spam message in chat',
|
||||
'pin [reply]*': 'Pin replied message',
|
||||
'unpin [reply]*': 'Unpin replied message',
|
||||
'ro': 'enable read-only mode',
|
||||
'unro': 'disable read-only mode',
|
||||
'antiraid [on|off]': 'when enabled, anyone who writes message will be blocked. Useful in raids. '
|
||||
'Running without arguments equals to toggling state',
|
||||
'welcome [text]*': 'enable auto-welcome to new users in groups. Running without text equals to disable',
|
||||
'kickdel': 'Kick all deleted accounts',
|
||||
}
|
||||
|
||||
+84
-104
@@ -14,12 +14,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from collections.abc import AsyncGenerator
|
||||
from time import perf_counter
|
||||
from typing import AsyncGenerator, List, Optional, Union
|
||||
from typing import Optional
|
||||
|
||||
from pyrogram import Client, enums, filters, raw, types, utils
|
||||
from pyrogram.types.object import Object
|
||||
|
||||
from utils.misc import modules_help, prefix
|
||||
from utils.scripts import format_exc
|
||||
|
||||
@@ -28,7 +28,7 @@ class Chat(Object):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
client: "Client" = None,
|
||||
client: 'Client' = None,
|
||||
id: id,
|
||||
type: type,
|
||||
is_verified: bool = None,
|
||||
@@ -41,7 +41,7 @@ class Chat(Object):
|
||||
username: str = None,
|
||||
first_name: str = None,
|
||||
last_name: str = None,
|
||||
photo: "types.ChatPhoto" = None,
|
||||
photo: 'types.ChatPhoto' = None,
|
||||
bio: str = None,
|
||||
description: str = None,
|
||||
dc_id: int = None,
|
||||
@@ -51,12 +51,12 @@ class Chat(Object):
|
||||
sticker_set_name: str = None,
|
||||
can_set_sticker_set: bool = None,
|
||||
members_count: int = None,
|
||||
restrictions: List["types.Restriction"] = None,
|
||||
permissions: "types.ChatPermissions" = None,
|
||||
restrictions: list['types.Restriction'] = None,
|
||||
permissions: 'types.ChatPermissions' = None,
|
||||
distance: int = None,
|
||||
linked_chat: "types.Chat" = None,
|
||||
send_as_chat: "types.Chat" = None,
|
||||
available_reactions: Optional["types.ChatReactions"] = None,
|
||||
linked_chat: 'types.Chat' = None,
|
||||
send_as_chat: 'types.Chat' = None,
|
||||
available_reactions: Optional['types.ChatReactions'] = None,
|
||||
is_admin: bool = False,
|
||||
deactivated: bool = False,
|
||||
):
|
||||
@@ -94,98 +94,82 @@ class Chat(Object):
|
||||
self.deactivated = deactivated
|
||||
|
||||
@staticmethod
|
||||
def _parse_user_chat(client, user: raw.types.User) -> "Chat":
|
||||
def _parse_user_chat(client, user: raw.types.User) -> 'Chat':
|
||||
peer_id = user.id
|
||||
|
||||
return Chat(
|
||||
id=peer_id,
|
||||
type=enums.ChatType.BOT if user.bot else enums.ChatType.PRIVATE,
|
||||
is_verified=getattr(user, "verified", None),
|
||||
is_restricted=getattr(user, "restricted", None),
|
||||
is_scam=getattr(user, "scam", None),
|
||||
is_fake=getattr(user, "fake", None),
|
||||
is_support=getattr(user, "support", None),
|
||||
is_verified=getattr(user, 'verified', None),
|
||||
is_restricted=getattr(user, 'restricted', None),
|
||||
is_scam=getattr(user, 'scam', None),
|
||||
is_fake=getattr(user, 'fake', None),
|
||||
is_support=getattr(user, 'support', None),
|
||||
username=user.username,
|
||||
first_name=user.first_name,
|
||||
last_name=user.last_name,
|
||||
photo=types.ChatPhoto._parse(client, user.photo, peer_id, user.access_hash),
|
||||
restrictions=types.List(
|
||||
[types.Restriction._parse(r) for r in user.restriction_reason]
|
||||
)
|
||||
or None,
|
||||
dc_id=getattr(getattr(user, "photo", None), "dc_id", None),
|
||||
restrictions=types.List([types.Restriction._parse(r) for r in user.restriction_reason]) or None,
|
||||
dc_id=getattr(getattr(user, 'photo', None), 'dc_id', None),
|
||||
client=client,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _parse_chat_chat(client, chat: raw.types.Chat) -> "Chat":
|
||||
def _parse_chat_chat(client, chat: raw.types.Chat) -> 'Chat':
|
||||
peer_id = -chat.id
|
||||
return Chat(
|
||||
id=peer_id,
|
||||
type=enums.ChatType.GROUP,
|
||||
title=chat.title,
|
||||
is_creator=getattr(chat, "creator", None),
|
||||
photo=types.ChatPhoto._parse(
|
||||
client, getattr(chat, "photo", None), peer_id, 0
|
||||
),
|
||||
permissions=types.ChatPermissions._parse(
|
||||
getattr(chat, "default_banned_rights", None)
|
||||
),
|
||||
members_count=getattr(chat, "participants_count", None),
|
||||
dc_id=getattr(getattr(chat, "photo", None), "dc_id", None),
|
||||
has_protected_content=getattr(chat, "noforwards", None),
|
||||
is_creator=getattr(chat, 'creator', None),
|
||||
photo=types.ChatPhoto._parse(client, getattr(chat, 'photo', None), peer_id, 0),
|
||||
permissions=types.ChatPermissions._parse(getattr(chat, 'default_banned_rights', None)),
|
||||
members_count=getattr(chat, 'participants_count', None),
|
||||
dc_id=getattr(getattr(chat, 'photo', None), 'dc_id', None),
|
||||
has_protected_content=getattr(chat, 'noforwards', None),
|
||||
client=client,
|
||||
is_admin=bool(getattr(chat, "admin_rights", False)),
|
||||
is_admin=bool(getattr(chat, 'admin_rights', False)),
|
||||
deactivated=chat.deactivated,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _parse_channel_chat(client, channel: raw.types.Channel) -> "Chat":
|
||||
def _parse_channel_chat(client, channel: raw.types.Channel) -> 'Chat':
|
||||
peer_id = utils.get_channel_id(channel.id)
|
||||
restriction_reason = getattr(channel, "restriction_reason", [])
|
||||
restriction_reason = getattr(channel, 'restriction_reason', [])
|
||||
|
||||
return Chat(
|
||||
id=peer_id,
|
||||
type=(
|
||||
enums.ChatType.SUPERGROUP
|
||||
if getattr(channel, "megagroup", None)
|
||||
else enums.ChatType.CHANNEL
|
||||
),
|
||||
is_verified=getattr(channel, "verified", None),
|
||||
is_restricted=getattr(channel, "restricted", None),
|
||||
is_creator=getattr(channel, "creator", None),
|
||||
is_scam=getattr(channel, "scam", None),
|
||||
is_fake=getattr(channel, "fake", None),
|
||||
type=(enums.ChatType.SUPERGROUP if getattr(channel, 'megagroup', None) else enums.ChatType.CHANNEL),
|
||||
is_verified=getattr(channel, 'verified', None),
|
||||
is_restricted=getattr(channel, 'restricted', None),
|
||||
is_creator=getattr(channel, 'creator', None),
|
||||
is_scam=getattr(channel, 'scam', None),
|
||||
is_fake=getattr(channel, 'fake', None),
|
||||
title=channel.title,
|
||||
username=getattr(channel, "username", None),
|
||||
username=getattr(channel, 'username', None),
|
||||
photo=types.ChatPhoto._parse(
|
||||
client,
|
||||
getattr(channel, "photo", None),
|
||||
getattr(channel, 'photo', None),
|
||||
peer_id,
|
||||
getattr(channel, "access_hash", 0),
|
||||
getattr(channel, 'access_hash', 0),
|
||||
),
|
||||
restrictions=types.List(
|
||||
[types.Restriction._parse(r) for r in restriction_reason]
|
||||
)
|
||||
or None,
|
||||
permissions=types.ChatPermissions._parse(
|
||||
getattr(channel, "default_banned_rights", None)
|
||||
),
|
||||
members_count=getattr(channel, "participants_count", None),
|
||||
dc_id=getattr(getattr(channel, "photo", None), "dc_id", None),
|
||||
has_protected_content=getattr(channel, "noforwards", None),
|
||||
is_admin=bool(getattr(channel, "admin_rights", False)),
|
||||
restrictions=types.List([types.Restriction._parse(r) for r in restriction_reason]) or None,
|
||||
permissions=types.ChatPermissions._parse(getattr(channel, 'default_banned_rights', None)),
|
||||
members_count=getattr(channel, 'participants_count', None),
|
||||
dc_id=getattr(getattr(channel, 'photo', None), 'dc_id', None),
|
||||
has_protected_content=getattr(channel, 'noforwards', None),
|
||||
is_admin=bool(getattr(channel, 'admin_rights', False)),
|
||||
client=client,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _parse(
|
||||
client,
|
||||
message: Union[raw.types.Message, raw.types.MessageService],
|
||||
message: raw.types.Message | raw.types.MessageService,
|
||||
users: dict,
|
||||
chats: dict,
|
||||
is_chat: bool,
|
||||
) -> "Chat":
|
||||
) -> 'Chat':
|
||||
from_id = utils.get_raw_peer_id(message.from_id)
|
||||
peer_id = utils.get_raw_peer_id(message.peer_id)
|
||||
chat_id = (peer_id or from_id) if is_chat else (from_id or peer_id)
|
||||
@@ -211,9 +195,9 @@ class Dialog(Object):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
client: "Client" = None,
|
||||
chat: "types.Chat",
|
||||
top_message: "types.Message",
|
||||
client: 'Client' = None,
|
||||
chat: 'types.Chat',
|
||||
top_message: 'types.Message',
|
||||
unread_messages_count: int,
|
||||
unread_mentions_count: int,
|
||||
unread_mark: bool,
|
||||
@@ -229,7 +213,7 @@ class Dialog(Object):
|
||||
self.is_pinned = is_pinned
|
||||
|
||||
@staticmethod
|
||||
def _parse(client, dialog: "raw.types.Dialog", messages, users, chats) -> "Dialog":
|
||||
def _parse(client, dialog: 'raw.types.Dialog', messages, users, chats) -> 'Dialog':
|
||||
return Dialog(
|
||||
chat=Chat._parse_dialog(client, dialog.peer, users, chats),
|
||||
top_message=messages.get(utils.get_peer_id(dialog.peer)),
|
||||
@@ -241,9 +225,7 @@ class Dialog(Object):
|
||||
)
|
||||
|
||||
|
||||
async def get_dialogs(
|
||||
self: "Client", limit: int = 0
|
||||
) -> Optional[AsyncGenerator["types.Dialog", None]]:
|
||||
async def get_dialogs(self: 'Client', limit: int = 0) -> AsyncGenerator['types.Dialog', None] | None:
|
||||
current = 0
|
||||
total = limit or (1 << 31) - 1
|
||||
limit = min(100, total)
|
||||
@@ -287,7 +269,7 @@ async def get_dialogs(
|
||||
return
|
||||
|
||||
|
||||
@Client.on_message(filters.command("admlist", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('admlist', prefix) & filters.me)
|
||||
async def admlist(client: Client, message: types.Message):
|
||||
await message.edit("<b>Retrieving information... (it'll take some time)</b>")
|
||||
|
||||
@@ -298,48 +280,46 @@ async def admlist(client: Client, message: types.Message):
|
||||
owned_usernamed_chats = []
|
||||
async for dialog in get_dialogs(client):
|
||||
chat = dialog.chat
|
||||
if getattr(chat, "deactivated", False):
|
||||
if getattr(chat, 'deactivated', False):
|
||||
continue
|
||||
if getattr(chat, "is_creator", False) and getattr(chat, "username", None):
|
||||
if getattr(chat, 'is_creator', False) and getattr(chat, 'username', None):
|
||||
owned_usernamed_chats.append(chat)
|
||||
elif getattr(chat, "is_creator", False):
|
||||
elif getattr(chat, 'is_creator', False):
|
||||
owned_chats.append(chat)
|
||||
elif getattr(chat, "is_admin", False):
|
||||
elif getattr(chat, 'is_admin', False):
|
||||
adminned_chats.append(chat)
|
||||
|
||||
text = "<b>Adminned chats:</b>\n"
|
||||
text = '<b>Adminned chats:</b>\n'
|
||||
for index, chat in enumerate(adminned_chats):
|
||||
cid = str(chat.id).replace("-100", "")
|
||||
text += f"{index + 1}. <a href=https://t.me/c/{cid}/1>{chat.title}</a>\n"
|
||||
cid = str(chat.id).replace('-100', '')
|
||||
text += f'{index + 1}. <a href=https://t.me/c/{cid}/1>{chat.title}</a>\n'
|
||||
|
||||
text += "\n<b>Owned chats:</b>\n"
|
||||
text += '\n<b>Owned chats:</b>\n'
|
||||
for index, chat in enumerate(owned_chats):
|
||||
cid = str(chat.id).replace("-100", "")
|
||||
text += f"{index + 1}. <a href=https://t.me/c/{cid}/1>{chat.title}</a>\n"
|
||||
cid = str(chat.id).replace('-100', '')
|
||||
text += f'{index + 1}. <a href=https://t.me/c/{cid}/1>{chat.title}</a>\n'
|
||||
|
||||
text += "\n<b>Owned chats with username:</b>\n"
|
||||
text += '\n<b>Owned chats with username:</b>\n'
|
||||
for index, chat in enumerate(owned_usernamed_chats):
|
||||
cid = str(chat.id).replace("-100", "")
|
||||
text += f"{index + 1}. <a href=https://t.me/c/{cid}/1>{chat.title}</a>\n"
|
||||
cid = str(chat.id).replace('-100', '')
|
||||
text += f'{index + 1}. <a href=https://t.me/c/{cid}/1>{chat.title}</a>\n'
|
||||
|
||||
stop = perf_counter()
|
||||
total_count = (
|
||||
len(adminned_chats) + len(owned_chats) + len(owned_usernamed_chats)
|
||||
)
|
||||
total_count = len(adminned_chats) + len(owned_chats) + len(owned_usernamed_chats)
|
||||
await message.edit(
|
||||
text + "\n"
|
||||
f"<b><u>Total:</u></b> {total_count}"
|
||||
f"\n<b><u>Adminned chats:</u></b> {len(adminned_chats)}\n"
|
||||
f"<b><u>Owned chats:</u></b> {len(owned_chats)}\n"
|
||||
f"<b><u>Owned chats with username:</u></b> {len(owned_usernamed_chats)}\n\n"
|
||||
f"Done in {round(stop - start, 3)} seconds.",
|
||||
text + '\n'
|
||||
f'<b><u>Total:</u></b> {total_count}'
|
||||
f'\n<b><u>Adminned chats:</u></b> {len(adminned_chats)}\n'
|
||||
f'<b><u>Owned chats:</u></b> {len(owned_chats)}\n'
|
||||
f'<b><u>Owned chats with username:</u></b> {len(owned_usernamed_chats)}\n\n'
|
||||
f'Done in {round(stop - start, 3)} seconds.',
|
||||
)
|
||||
except Exception as e:
|
||||
await message.edit(format_exc(e))
|
||||
return
|
||||
|
||||
|
||||
@Client.on_message(filters.command("admcount", prefix) & filters.me)
|
||||
@Client.on_message(filters.command('admcount', prefix) & filters.me)
|
||||
async def admcount(client: Client, message: types.Message):
|
||||
await message.edit("<b>Retrieving information... (it'll take some time)</b>")
|
||||
|
||||
@@ -350,31 +330,31 @@ async def admcount(client: Client, message: types.Message):
|
||||
owned_usernamed_chats = 0
|
||||
async for dialog in get_dialogs(client):
|
||||
chat = dialog.chat
|
||||
if getattr(chat, "deactivated", False):
|
||||
if getattr(chat, 'deactivated', False):
|
||||
continue
|
||||
if getattr(chat, "is_creator", False) and getattr(chat, "username", None):
|
||||
if getattr(chat, 'is_creator', False) and getattr(chat, 'username', None):
|
||||
owned_usernamed_chats += 1
|
||||
elif getattr(chat, "is_creator", False):
|
||||
elif getattr(chat, 'is_creator', False):
|
||||
owned_chats += 1
|
||||
elif getattr(chat, "is_admin", False):
|
||||
elif getattr(chat, 'is_admin', False):
|
||||
adminned_chats += 1
|
||||
|
||||
stop = perf_counter()
|
||||
total_count = adminned_chats + owned_chats + owned_usernamed_chats
|
||||
await message.edit(
|
||||
f"<b><u>Total:</u></b> {total_count}"
|
||||
f"\n<b><u>Adminned chats:</u></b> {adminned_chats}\n"
|
||||
f"<b><u>Owned chats:</u></b> {owned_chats}\n"
|
||||
f"<b><u>Owned chats with username:</u></b> {owned_usernamed_chats}\n\n"
|
||||
f"Done in {round(stop - start, 3)} seconds.\n\n"
|
||||
f"<b>Get full list: </b><code>{prefix}admlist</code>",
|
||||
f'<b><u>Total:</u></b> {total_count}'
|
||||
f'\n<b><u>Adminned chats:</u></b> {adminned_chats}\n'
|
||||
f'<b><u>Owned chats:</u></b> {owned_chats}\n'
|
||||
f'<b><u>Owned chats with username:</u></b> {owned_usernamed_chats}\n\n'
|
||||
f'Done in {round(stop - start, 3)} seconds.\n\n'
|
||||
f'<b>Get full list: </b><code>{prefix}admlist</code>',
|
||||
)
|
||||
except Exception as e:
|
||||
await message.edit(format_exc(e))
|
||||
return
|
||||
|
||||
|
||||
modules_help["admlist"] = {
|
||||
"admcount": "Get count of adminned and owned chats",
|
||||
"admlist": "Get list of adminned and owned chats",
|
||||
modules_help['admlist'] = {
|
||||
'admcount': 'Get count of adminned and owned chats',
|
||||
'admlist': 'Get list of adminned and owned chats',
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user