Skip to content

Commit

Permalink
Merge pull request #637 from UnitapApp/feature/telegram/implementation
Browse files Browse the repository at this point in the history
Feature/telegram/implementation
  • Loading branch information
ShayanShiravani authored Oct 26, 2024
2 parents 215e620 + 70fbcfa commit e022f58
Show file tree
Hide file tree
Showing 33 changed files with 1,199 additions and 5 deletions.
1 change: 1 addition & 0 deletions .github/workflows/django.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,4 @@ jobs:
CONSUMER_KEY: ${{ secrets.CONSUMER_KEY }}
CONSUMER_SECRET: ${{ secrets.CONSUMER_SECRET }}
DEPLOYMENT_ENV: "dev"
TELEGRAM_BOT_API_KEY: ${{ secrets.TELEGRAM_BOT_API_KEY }}
11 changes: 9 additions & 2 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 @@ -258,8 +265,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
36 changes: 36 additions & 0 deletions core/thirdpartyapp/telegram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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

return time.time() - int(auth_data["auth_date"]) <= 86400


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.
48 changes: 48 additions & 0 deletions telegram/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# 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
from django.core.exceptions import PermissionDenied


@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

@admin.action(
description="Broadcast message to all users",
permissions=["telegram.can_broadcast"],
)
def broadcast_view(self, request):
if not request.user.has_perm("telegram.can_broadcast"):
raise PermissionDenied("You do not have permission to broadcast messages.")

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})
else:
form = BroadcastMessageForm()

return render(request, "admin/broadcast.html", {"form": form})
20 changes: 20 additions & 0 deletions telegram/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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
from telegram import messages

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

return super().ready()
Loading

0 comments on commit e022f58

Please sign in to comment.