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 all 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
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 }}
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
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):
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved
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})
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved
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
Loading