Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/telegram/implementation #637

Merged
merged 21 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions brightIDfaucet/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ def str2bool(v):
MEMCACHED_PASSWORD = os.environ.get("MEMCACHEDCLOUD_PASSWORD")
DEPLOYMENT_ENV = os.environ.get("DEPLOYMENT_ENV")


TELEGRAM_BOT_API_KEY = os.environ.get("TELEGRAM_BOT_API_KEY")
TELEGRAM_BOT_USERNAME = os.environ.get("TELEGRAM_BOT_USERNAME")
TELEGRAM_BOT_API_SECRET = os.environ.get("TELEGRAM_BOT_API_SECRET")
TELEGRAM_BUG_REPORTER_CHANNEL_ID = os.environ.get("TELEGRAM_BUG_REPORTER_CHANNEL_ID")

CLOUDFLARE_IMAGES_ACCOUNT_ID = os.environ.get("CLOUDFLARE_ACCOUNT_ID")
CLOUDFLARE_IMAGES_API_TOKEN = os.environ.get("CLOUDFLARE_API_TOKEN")
CLOUDFLARE_IMAGES_ACCOUNT_HASH = os.environ.get("CLOUDFLARE_ACCOUNT_HASH")
Expand Down Expand Up @@ -135,6 +141,7 @@ def before_send(event, hint):
"corsheaders",
"django_filters",
"safedelete",
"telegram.apps.TelegramConfig",
]

MIDDLEWARE = [
Expand Down Expand Up @@ -171,7 +178,7 @@ def before_send(event, hint):
WSGI_APPLICATION = "brightIDfaucet.wsgi.application"

STORAGES = {
"default": {
"default": {
"BACKEND": "cloudflare_images.storage.CloudflareImagesStorage",
},
"staticfiles": { # default
Expand Down Expand Up @@ -251,8 +258,8 @@ def before_send(event, hint):
# These headers are required for Cloudflare and HCaptcha Turnstile anti-bot service

CORS_ALLOW_HEADERS = list(default_headers) + [
'cf-turnstile-response',
'hc-turnstile-response',
"cf-turnstile-response",
"hc-turnstile-response",
]

# Static files (CSS, JavaScript, Images)
Expand Down
2 changes: 2 additions & 0 deletions brightIDfaucet/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""

from django.contrib import admin
from django.urls import include, path

Expand All @@ -31,4 +32,5 @@
path("api/prizetap/", include("prizetap.urls")),
path("api/quiztap/", include("quiztap.urls")),
path("api/analytics/", include("analytics.urls")),
path("api/telegram/", include("telegram.urls")),
]
1 change: 1 addition & 0 deletions core/constraints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
IsFollowingTwitterUser,
)
from core.constraints.zora import DidMintZoraNFT
from core.constraints.telegram import HasTelegramConnection


def get_constraint(constraint_label: str) -> ConstraintVerification:
Expand Down
23 changes: 23 additions & 0 deletions core/constraints/telegram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import logging

from core.constraints.abstract import (
ConstraintApp,
ConstraintVerification,
)


logger = logging.getLogger(__name__)


class HasTelegramConnection(ConstraintVerification):
_param_keys = []
app_name = ConstraintApp.GENERAL.value

def is_observed(self, *args, **kwargs) -> bool:
from telegram.models import TelegramConnection

try:
twitter = TelegramConnection.get_connection(self.user_profile)
except TelegramConnection.DoesNotExist:
return False
return twitter.is_connected()
4 changes: 3 additions & 1 deletion core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
IsFollowingLensUser,
IsFollowingTwitterBatch,
IsFollowingTwitterUser,
HasTelegramConnection,
)
from .utils import SolanaWeb3Utils, Web3Utils

Expand Down Expand Up @@ -160,7 +161,8 @@ class Type(models.TextChoices):
IsFollowingFarcasterBatch,
HasVerifiedCloudflareCaptcha,
DidMintZoraNFT,
HasVerifiedHCaptcha
HasVerifiedHCaptcha,
HasTelegramConnection,
]

name = models.CharField(
Expand Down
40 changes: 40 additions & 0 deletions core/thirdpartyapp/telegram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from django.conf import settings

import hashlib
import hmac
import time


def verify_telegram_auth(bot_token, data):
auth_data = dict(data)
hash_check = auth_data.pop("hash")

# Create the data string by sorting keys and concatenating key=value pairs
data_check_string = "\n".join([f"{k}={v}" for k, v in sorted(auth_data.items())])

# Hash the data string with your bot's token
secret_key = hashlib.sha256(bot_token.encode()).digest()
calculated_hash = hmac.new(
secret_key, data_check_string.encode(), hashlib.sha256
).hexdigest()

# Compare the calculated hash with the received hash
if calculated_hash != hash_check:
return False

# Optional: Check that the authentication data is recent (within a day)
if time.time() - int(auth_data["auth_date"]) > 86400:
return False

return True
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved


class TelegramUtil:
bot_token = settings.TELEGRAM_BOT_API_KEY
bot_username = settings.TELEGRAM_BOT_USERNAME

def __init__(self) -> None:
pass

def verify_login(self, telegram_data):
return verify_telegram_auth(self.bot_token, telegram_data)
Empty file modified prizetap/migrations/0067_alter_constraint_name.py
100644 → 100755
Empty file.
93 changes: 93 additions & 0 deletions prizetap/migrations/0081_alter_constraint_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Generated by Django 5.1.2 on 2024-10-17 11:39

from django.db import migrations, models


def create_prizetap_constraint(apps, schema_editor):
Constraint = apps.get_model("prizetap", "Constraint")

Constraint.objects.create(
name="core.HasTelegramConnection",
description="HasTelegramConnection",
title="Connect Telegram",
type="VER",
)


class Migration(migrations.Migration):

dependencies = [
("prizetap", "0080_alter_constraint_name"),
]

operations = [
migrations.AlterField(
model_name="constraint",
name="name",
field=models.CharField(
choices=[
("core.BrightIDMeetVerification", "BrightIDMeetVerification"),
("core.BrightIDAuraVerification", "BrightIDAuraVerification"),
("core.HasNFTVerification", "HasNFTVerification"),
("core.HasTokenVerification", "HasTokenVerification"),
(
"core.HasTokenTransferVerification",
"HasTokenTransferVerification",
),
("core.AllowListVerification", "AllowListVerification"),
("core.HasENSVerification", "HasENSVerification"),
("core.HasLensProfile", "HasLensProfile"),
("core.IsFollowingLensUser", "IsFollowingLensUser"),
("core.BeFollowedByLensUser", "BeFollowedByLensUser"),
("core.DidMirrorOnLensPublication", "DidMirrorOnLensPublication"),
("core.DidCollectLensPublication", "DidCollectLensPublication"),
("core.HasMinimumLensPost", "HasMinimumLensPost"),
("core.HasMinimumLensFollower", "HasMinimumLensFollower"),
("core.BeFollowedByFarcasterUser", "BeFollowedByFarcasterUser"),
("core.HasMinimumFarcasterFollower", "HasMinimumFarcasterFollower"),
("core.DidLikedFarcasterCast", "DidLikedFarcasterCast"),
("core.DidRecastFarcasterCast", "DidRecastFarcasterCast"),
("core.IsFollowingFarcasterUser", "IsFollowingFarcasterUser"),
("core.HasFarcasterProfile", "HasFarcasterProfile"),
("core.BeAttestedBy", "BeAttestedBy"),
("core.Attest", "Attest"),
("core.HasDonatedOnGitcoin", "HasDonatedOnGitcoin"),
("core.HasMinimumHumanityScore", "HasMinimumHumanityScore"),
("core.HasGitcoinPassportProfile", "HasGitcoinPassportProfile"),
("core.IsFollowingFarcasterChannel", "IsFollowingFarcasterChannel"),
("core.BridgeEthToArb", "BridgeEthToArb"),
("core.IsFollowingTwitterUser", "IsFollowingTwitterUser"),
("core.BeFollowedByTwitterUser", "BeFollowedByTwitterUser"),
("core.DidRetweetTweet", "DidRetweetTweet"),
("core.DidQuoteTweet", "DidQuoteTweet"),
("core.HasMuonNode", "HasMuonNode"),
("core.DelegateArb", "DelegateArb"),
("core.DelegateOP", "DelegateOP"),
("core.DidDelegateArbToAddress", "DidDelegateArbToAddress"),
("core.DidDelegateOPToAddress", "DidDelegateOPToAddress"),
("core.GLMStakingVerification", "GLMStakingVerification"),
("core.IsFollowingTwitterBatch", "IsFollowingTwitterBatch"),
("core.IsFollowingFarcasterBatch", "IsFollowingFarcasterBatch"),
(
"core.HasVerifiedCloudflareCaptcha",
"HasVerifiedCloudflareCaptcha",
),
("core.DidMintZoraNFT", "DidMintZoraNFT"),
("core.HasVerifiedHCaptcha", "HasVerifiedHCaptcha"),
("core.HasTelegramConnection", "HasTelegramConnection"),
("prizetap.HaveUnitapPass", "HaveUnitapPass"),
("prizetap.NotHaveUnitapPass", "NotHaveUnitapPass"),
("faucet.OptimismDonationConstraint", "OptimismDonationConstraint"),
(
"faucet.OptimismClaimingGasConstraint",
"OptimismClaimingGasConstraint",
),
],
max_length=255,
unique=True,
),
),
migrations.RunPython(
create_prizetap_constraint, reverse_code=migrations.RunPython.noop
),
]
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
bip-utils==2.3.0
django==5
django==5.1.2
anchorpy==0.15.0
djangorestframework==3.15.2
djangorestframework-camel-case==1.3.0
Expand Down Expand Up @@ -35,3 +35,5 @@ ratelimit~=2.2.1
pillow==10.4.0
django-cloudflare-images~=0.6.0
zstandard~=0.17.0
pyTelegramBotAPI==4.23.0
django-telegram-login==0.2.3
2 changes: 1 addition & 1 deletion sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ SECRET_KEY="django-insecure-!=_mi0j#rhk7c9p-0wg-3me6y&fk$+fahz6fh)k1n#&@s(9vf5"
BRIGHT_PRIVATE_KEY=""
DEBUG="True"
SENTRY_DSN="DEBUG-DSN"
DEPLOYMENT_ENV="env"
DEPLOYMENT_ENV="dev"
Empty file added telegram/__init__.py
Empty file.
40 changes: 40 additions & 0 deletions telegram/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# admin.py
from django.contrib import admin
from django.urls import path
from django.shortcuts import render
from .models import TelegramConnection
from .forms import BroadcastMessageForm
from telegram.bot import TelegramMessenger


@admin.register(TelegramConnection)
class TelegramConnectionAdmin(admin.ModelAdmin):
list_display = ("pk", "user_profile", "user_id") # Adjust as per your model fields

def get_urls(self):
urls = super().get_urls()
custom_urls = [
path(
"broadcast/",
self.admin_site.admin_view(self.broadcast_view),
name="broadcast_message",
),
]
return custom_urls + urls

def broadcast_view(self, request):
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved
if request.method == "POST":
form = BroadcastMessageForm(request.POST)
if form.is_valid():
message = form.cleaned_data["message"]
users = TelegramConnection.objects.all()
messenger = TelegramMessenger.get_instance()
for user in users:
messenger.send_message(user.user_id, text=message)

self.message_user(request, "Message sent to all users!")
return render(request, "admin/broadcast.html", {"form": form})
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved
else:
form = BroadcastMessageForm()

return render(request, "admin/broadcast.html", {"form": form})
19 changes: 19 additions & 0 deletions telegram/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.apps import AppConfig
from django.conf import settings


class TelegramConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "telegram"

def ready(self) -> None:
if settings.DEPLOYMENT_ENV == "DEV":
return super().ready()

from .bot import TelegramMessenger

messenger = TelegramMessenger.get_instance()
messenger.ensure_webhook()
messenger.ready()

return super().ready()
Loading
Loading