feat(telegram_bot): added campaign creation and updating, added help command
This commit is contained in:
@@ -18,10 +18,12 @@ class NotFoundError(BaseModel):
|
|||||||
|
|
||||||
class CampaignTargeting(BaseModel):
|
class CampaignTargeting(BaseModel):
|
||||||
gender: Literal["MALE", "FEMALE", "ALL"] | None = None
|
gender: Literal["MALE", "FEMALE", "ALL"] | None = None
|
||||||
age_from: Annotated[NonNegativeInt, Field(strict=True, ls=100)] | None = (
|
age_from: Annotated[NonNegativeInt, Field(strict=False, ls=100)] | None = (
|
||||||
|
None
|
||||||
|
)
|
||||||
|
age_to: Annotated[NonNegativeInt, Field(strict=False, ls=100)] | None = (
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
age_to: Annotated[NonNegativeInt, Field(strict=True, ls=100)] | None = None
|
|
||||||
location: str | None = None
|
location: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
from aiogram import Router
|
||||||
|
from aiogram.filters import Command
|
||||||
|
from aiogram.fsm.context import FSMContext
|
||||||
|
from aiogram.types import Message
|
||||||
|
from aiogram_dialog import DialogManager
|
||||||
|
|
||||||
|
help_router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
@help_router.message(Command("help"))
|
||||||
|
async def help_command(
|
||||||
|
message: Message, dialog_manager: DialogManager, state: FSMContext
|
||||||
|
) -> None:
|
||||||
|
response = (
|
||||||
|
"Commands:\n\n"
|
||||||
|
"/start - Start the bot and authenticate as advertiser\n"
|
||||||
|
"/campaigns - Manage your campaigns\n"
|
||||||
|
"/statistics - See your overall statistics\n"
|
||||||
|
"/logout - Logout of current advertiser account"
|
||||||
|
)
|
||||||
|
await message.answer(response)
|
||||||
@@ -19,7 +19,8 @@ async def start_command(
|
|||||||
await message.answer(
|
await message.answer(
|
||||||
"Already authenticated as"
|
"Already authenticated as"
|
||||||
f" <code>{state_data['advertiser']['name']}</code> "
|
f" <code>{state_data['advertiser']['name']}</code> "
|
||||||
f"(<code>{state_data['advertiser']['advertiser_id']}</code>)"
|
f"(<code>{state_data['advertiser']['advertiser_id']}</code>)."
|
||||||
|
"Get all commands with /help."
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ async def stats_command(
|
|||||||
f"\t• Impressions: {stats.impressions_count}\n"
|
f"\t• Impressions: {stats.impressions_count}\n"
|
||||||
f"\t• Clicks: {stats.clicks_count}\n"
|
f"\t• Clicks: {stats.clicks_count}\n"
|
||||||
f"\t• Conversion: {stats.conversion:.2f}%\n"
|
f"\t• Conversion: {stats.conversion:.2f}%\n"
|
||||||
f"\t• Spent on impressions: ${stats.spent_impressions:.2f}\n"
|
f"\t• Spent on impressions: {stats.spent_impressions:.2f}\n"
|
||||||
f"\t• Spent on clicks: ${stats.spent_clicks:.2f}\n"
|
f"\t• Spent on clicks: {stats.spent_clicks:.2f}\n"
|
||||||
f"\t• Spent total: ${stats.spent_total:.2f}"
|
f"\t• Spent total: {stats.spent_total:.2f}"
|
||||||
)
|
)
|
||||||
await message.answer(response)
|
await message.answer(response)
|
||||||
|
|||||||
@@ -6,18 +6,72 @@ from urllib.parse import urlparse
|
|||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from aiogram.fsm.context import FSMContext
|
from aiogram.fsm.context import FSMContext
|
||||||
from aiogram.types import CallbackQuery, ContentType
|
from aiogram.types import CallbackQuery, ContentType, Message
|
||||||
from aiogram_dialog import Dialog, DialogManager, Window
|
from aiogram_dialog import Dialog, DialogManager, Window
|
||||||
from aiogram_dialog.api.entities import MediaAttachment
|
from aiogram_dialog.api.entities import MediaAttachment
|
||||||
from aiogram_dialog.widgets.common import Whenable
|
from aiogram_dialog.widgets.common import Whenable
|
||||||
|
from aiogram_dialog.widgets.input import ManagedTextInput, TextInput
|
||||||
from aiogram_dialog.widgets.kbd import Button, ListGroup, ScrollingGroup, Start
|
from aiogram_dialog.widgets.kbd import Button, ListGroup, ScrollingGroup, Start
|
||||||
from aiogram_dialog.widgets.media import DynamicMedia
|
from aiogram_dialog.widgets.media import DynamicMedia
|
||||||
from aiogram_dialog.widgets.text import Const, Format
|
from aiogram_dialog.widgets.text import Const, Format
|
||||||
|
from pydantic import ValidationError
|
||||||
|
|
||||||
import config
|
import config
|
||||||
from api.client import AdNovaClient
|
from api.client import AdNovaClient
|
||||||
|
from api.errors import BadRequestError, ForbiddenError
|
||||||
|
from dialogs import utils
|
||||||
from states.campaigns import CampaignsDailogState
|
from states.campaigns import CampaignsDailogState
|
||||||
|
|
||||||
|
campaign_info = (
|
||||||
|
Const('<pre><code class="language-input-format">Title\n'),
|
||||||
|
Const("Text\n"),
|
||||||
|
Const("Impressions limit\n"),
|
||||||
|
Const("Clicks limit\n"),
|
||||||
|
Const("Cost per impression\n"),
|
||||||
|
Const("Cost per click\n"),
|
||||||
|
Const("Start date\n"),
|
||||||
|
Const("End date\n"),
|
||||||
|
Const("Targeting gender (ALL, FEMALE, MALE, None)\n"),
|
||||||
|
Const("Targeting age from (could be None)\n"),
|
||||||
|
Const("Targeting age to (could be None)\n"),
|
||||||
|
Const("Targeting location (could be None)"),
|
||||||
|
Const("</code></pre>"),
|
||||||
|
Const('<pre><code class="language-example">Some title\n'),
|
||||||
|
Const("Some text\n"),
|
||||||
|
Const("15\n"),
|
||||||
|
Const("10\n"),
|
||||||
|
Const("0.4\n"),
|
||||||
|
Const("0.5\n"),
|
||||||
|
Const("100\n"),
|
||||||
|
Const("110\n"),
|
||||||
|
Const("ALL\n"),
|
||||||
|
Const("12\n"),
|
||||||
|
Const("None\n"),
|
||||||
|
Const("Moscow"),
|
||||||
|
Const("</code></pre>"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def campaign_has_ad_image(
|
||||||
|
data: dict, widget: Whenable, manager: DialogManager
|
||||||
|
) -> bool:
|
||||||
|
return bool(data["dialog_data"]["campaign"]["ad_image"])
|
||||||
|
|
||||||
|
|
||||||
|
def campaign_has_not_ad_image(
|
||||||
|
data: dict, widget: Whenable, manager: DialogManager
|
||||||
|
) -> bool:
|
||||||
|
return not data["dialog_data"]["campaign"]["ad_image"]
|
||||||
|
|
||||||
|
|
||||||
|
def check_campaign(campaign_data: str) -> None:
|
||||||
|
fields = campaign_data.split("\n\n")
|
||||||
|
|
||||||
|
if len(fields) != 12:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
utils.campaign_from_list(fields)
|
||||||
|
|
||||||
|
|
||||||
async def campaigns(**kwargs: dict[Any]) -> dict[str, Any]:
|
async def campaigns(**kwargs: dict[Any]) -> dict[str, Any]:
|
||||||
state: FSMContext = kwargs["state"]
|
state: FSMContext = kwargs["state"]
|
||||||
@@ -37,50 +91,10 @@ async def campaigns(**kwargs: dict[Any]) -> dict[str, Any]:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def campaign_detail_on_click(
|
|
||||||
callback: CallbackQuery, button: Button, manager: DialogManager
|
|
||||||
) -> None:
|
|
||||||
manager_data: dict[Any] = await manager.load_data()
|
|
||||||
state: FSMContext = manager_data["middleware_data"]["state"]
|
|
||||||
state_data = await state.get_data()
|
|
||||||
|
|
||||||
advertiser_id = state_data["advertiser_id"]
|
|
||||||
campaign_id = manager.item_id
|
|
||||||
|
|
||||||
if campaign_id == "":
|
|
||||||
return
|
|
||||||
|
|
||||||
async with AdNovaClient() as client:
|
|
||||||
campaign = await client.get_campaign(
|
|
||||||
advertiser_id=advertiser_id,
|
|
||||||
campaign_id=campaign_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
if campaign.ad_image:
|
|
||||||
campaign.ad_image = (
|
|
||||||
f"{config.MINIO_URL}{urlparse(campaign.ad_image).path}"
|
|
||||||
)
|
|
||||||
|
|
||||||
await manager.update({"campaign": campaign.model_dump(mode="json")})
|
|
||||||
await callback.answer()
|
|
||||||
await manager.switch_to(CampaignsDailogState.campaign)
|
|
||||||
|
|
||||||
|
|
||||||
def campaign_has_ad_image(
|
|
||||||
data: dict, widget: Whenable, manager: DialogManager
|
|
||||||
) -> bool:
|
|
||||||
return bool(data["dialog_data"]["campaign"]["ad_image"])
|
|
||||||
|
|
||||||
|
|
||||||
def campaign_has_not_ad_image(
|
|
||||||
data: dict, widget: Whenable, manager: DialogManager
|
|
||||||
) -> bool:
|
|
||||||
return not data["dialog_data"]["campaign"]["ad_image"]
|
|
||||||
|
|
||||||
|
|
||||||
async def campaign_by_id(**kwargs: dict[Any]) -> dict[str, Any]:
|
async def campaign_by_id(**kwargs: dict[Any]) -> dict[str, Any]:
|
||||||
manager: DialogManager = kwargs["dialog_manager"]
|
manager: DialogManager = kwargs["dialog_manager"]
|
||||||
manager_data = await manager.load_data()
|
manager_data = await manager.load_data()
|
||||||
|
|
||||||
ad_image_url = manager_data["dialog_data"]["campaign"]["ad_image"]
|
ad_image_url = manager_data["dialog_data"]["campaign"]["ad_image"]
|
||||||
|
|
||||||
if not ad_image_url:
|
if not ad_image_url:
|
||||||
@@ -109,6 +123,146 @@ async def campaign_by_id(**kwargs: dict[Any]) -> dict[str, Any]:
|
|||||||
return {"ad_image": attachment}
|
return {"ad_image": attachment}
|
||||||
|
|
||||||
|
|
||||||
|
async def campaign_on_error(
|
||||||
|
message: Message,
|
||||||
|
widget: ManagedTextInput,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
error: object,
|
||||||
|
) -> None:
|
||||||
|
if isinstance(error, ValidationError):
|
||||||
|
await message.answer(f"Invalid campaign data: {error.json()}")
|
||||||
|
elif isinstance(error, ValueError):
|
||||||
|
await message.answer(f"Invalid campaign data {error!s}")
|
||||||
|
|
||||||
|
|
||||||
|
async def campaign_create_on_success(
|
||||||
|
message: Message,
|
||||||
|
widget: ManagedTextInput,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
campaign_data: str,
|
||||||
|
) -> None:
|
||||||
|
state = dialog_manager.middleware_data["state"]
|
||||||
|
state_data = await state.get_data()
|
||||||
|
|
||||||
|
fields = message.text.split("\n\n")
|
||||||
|
|
||||||
|
campaign = utils.campaign_from_list(fields)
|
||||||
|
|
||||||
|
async with AdNovaClient() as client:
|
||||||
|
try:
|
||||||
|
await client.create_campaign(
|
||||||
|
advertiser_id=state_data["advertiser_id"], data=campaign
|
||||||
|
)
|
||||||
|
await dialog_manager.switch_to(CampaignsDailogState.campaigns)
|
||||||
|
except BadRequestError as e:
|
||||||
|
await message.answer(
|
||||||
|
f"Invalid data: {e.model_dump(mode='json')['detail']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def campaign_edit_on_success(
|
||||||
|
message: Message,
|
||||||
|
widget: ManagedTextInput,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
campaign_data: str,
|
||||||
|
) -> None:
|
||||||
|
fields = message.text.split("\n\n")
|
||||||
|
|
||||||
|
new_campaign = utils.campaign_from_list(fields)
|
||||||
|
|
||||||
|
state = dialog_manager.middleware_data["state"]
|
||||||
|
state_data = await state.get_data()
|
||||||
|
|
||||||
|
manager_data: dict[Any] = await dialog_manager.load_data()
|
||||||
|
|
||||||
|
campaign_id = manager_data["dialog_data"]["campaign"]["campaign_id"]
|
||||||
|
|
||||||
|
async with AdNovaClient() as client:
|
||||||
|
try:
|
||||||
|
new_campaign = await client.update_campaign(
|
||||||
|
advertiser_id=state_data["advertiser_id"],
|
||||||
|
campaign_id=campaign_id,
|
||||||
|
data=new_campaign,
|
||||||
|
)
|
||||||
|
await dialog_manager.update(
|
||||||
|
{
|
||||||
|
"campaign": new_campaign.model_dump(mode="json"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await dialog_manager.switch_to(CampaignsDailogState.campaign)
|
||||||
|
except BadRequestError as e:
|
||||||
|
await message.answer(
|
||||||
|
f"Invalid data: {e.model_dump(mode='json')['detail']}"
|
||||||
|
)
|
||||||
|
except ForbiddenError as e:
|
||||||
|
await message.answer(
|
||||||
|
"Forbidden changing campaign: "
|
||||||
|
f"{e.model_dump(mode='json')['detail']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def campaign_detail_on_click(
|
||||||
|
callback: CallbackQuery, button: Button, manager: DialogManager
|
||||||
|
) -> None:
|
||||||
|
manager_data: dict[Any] = await manager.load_data()
|
||||||
|
state: FSMContext = manager_data["middleware_data"]["state"]
|
||||||
|
state_data = await state.get_data()
|
||||||
|
|
||||||
|
advertiser_id = state_data["advertiser_id"]
|
||||||
|
campaign_id = manager.item_id
|
||||||
|
|
||||||
|
if campaign_id == "":
|
||||||
|
return
|
||||||
|
|
||||||
|
async with AdNovaClient() as client:
|
||||||
|
campaign = await client.get_campaign(
|
||||||
|
advertiser_id=advertiser_id,
|
||||||
|
campaign_id=campaign_id,
|
||||||
|
)
|
||||||
|
campaign_statistics = await client.get_campaign_statistics(
|
||||||
|
campaign_id=campaign_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if campaign.ad_image:
|
||||||
|
campaign.ad_image = (
|
||||||
|
f"{config.MINIO_URL}{urlparse(campaign.ad_image).path}"
|
||||||
|
)
|
||||||
|
|
||||||
|
await manager.update(
|
||||||
|
{
|
||||||
|
"campaign": campaign.model_dump(mode="json"),
|
||||||
|
"campaign_statistics": campaign_statistics.model_dump(mode="json"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await callback.answer()
|
||||||
|
await manager.switch_to(CampaignsDailogState.campaign)
|
||||||
|
|
||||||
|
|
||||||
|
async def campaign_edit_on_click(
|
||||||
|
callback: CallbackQuery, button: Button, manager: DialogManager
|
||||||
|
) -> None:
|
||||||
|
manager_data: dict[Any] = await manager.load_data()
|
||||||
|
state: FSMContext = manager_data["middleware_data"]["state"]
|
||||||
|
state_data = await state.get_data()
|
||||||
|
|
||||||
|
advertiser_id = state_data["advertiser_id"]
|
||||||
|
campaign_id = manager_data["dialog_data"]["campaign"]["campaign_id"]
|
||||||
|
|
||||||
|
async with AdNovaClient() as client:
|
||||||
|
campaign = await client.get_campaign(
|
||||||
|
advertiser_id=advertiser_id,
|
||||||
|
campaign_id=campaign_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
await manager.update(
|
||||||
|
{
|
||||||
|
"campaign": campaign.model_dump(mode="json"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await callback.answer()
|
||||||
|
await manager.switch_to(CampaignsDailogState.campaign_edit)
|
||||||
|
|
||||||
|
|
||||||
async def delete_ad_image(
|
async def delete_ad_image(
|
||||||
callback: CallbackQuery, button: Button, manager: DialogManager
|
callback: CallbackQuery, button: Button, manager: DialogManager
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -150,16 +304,40 @@ async def delete_campaign(
|
|||||||
await manager.switch_to(CampaignsDailogState.campaigns)
|
await manager.switch_to(CampaignsDailogState.campaigns)
|
||||||
|
|
||||||
|
|
||||||
async def back_to_list(
|
async def back_to_campaign(
|
||||||
callback: CallbackQuery, button: Button, manager: DialogManager
|
callback: CallbackQuery, button: Button, manager: DialogManager
|
||||||
) -> None:
|
) -> None:
|
||||||
await manager.switch_to(CampaignsDailogState.campaigns)
|
manager_data = await manager.load_data()
|
||||||
|
state: FSMContext = manager_data["middleware_data"]["state"]
|
||||||
|
state_data = await state.get_data()
|
||||||
|
|
||||||
|
campaign = manager_data["dialog_data"]["campaign"]
|
||||||
|
campaign_id = campaign["campaign_id"]
|
||||||
|
advertiser_id = state_data["advertiser_id"]
|
||||||
|
|
||||||
|
async with AdNovaClient() as client:
|
||||||
|
campaign = await client.get_campaign(
|
||||||
|
advertiser_id=advertiser_id,
|
||||||
|
campaign_id=campaign_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
await manager.update(
|
||||||
|
{
|
||||||
|
"campaign": campaign.model_dump(mode="json"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await callback.answer()
|
||||||
|
await manager.switch_to(CampaignsDailogState.campaign)
|
||||||
|
|
||||||
|
|
||||||
campaigns_dialog = Dialog(
|
campaigns_dialog = Dialog(
|
||||||
Window(
|
Window(
|
||||||
Const("Campaigns:"),
|
Const("Campaigns:"),
|
||||||
Button(Const("➕ Create"), id="create_campaign"),
|
Start(
|
||||||
|
Const("➕ Create"),
|
||||||
|
id="create_campaign",
|
||||||
|
state=CampaignsDailogState.campaign_create,
|
||||||
|
),
|
||||||
ScrollingGroup(
|
ScrollingGroup(
|
||||||
ListGroup(
|
ListGroup(
|
||||||
Button(
|
Button(
|
||||||
@@ -178,6 +356,25 @@ campaigns_dialog = Dialog(
|
|||||||
state=CampaignsDailogState.campaigns,
|
state=CampaignsDailogState.campaigns,
|
||||||
getter=campaigns,
|
getter=campaigns,
|
||||||
),
|
),
|
||||||
|
Window(
|
||||||
|
Const(
|
||||||
|
"Enter campaign info in following format "
|
||||||
|
"(each statement on new line with one enter between each):"
|
||||||
|
),
|
||||||
|
*campaign_info,
|
||||||
|
Start(
|
||||||
|
Const("⬅️ Back to list"),
|
||||||
|
id="back",
|
||||||
|
state=CampaignsDailogState.campaigns,
|
||||||
|
),
|
||||||
|
TextInput(
|
||||||
|
id="campaign",
|
||||||
|
type_factory=check_campaign,
|
||||||
|
on_success=campaign_create_on_success,
|
||||||
|
on_error=campaign_on_error,
|
||||||
|
),
|
||||||
|
state=CampaignsDailogState.campaign_create,
|
||||||
|
),
|
||||||
Window(
|
Window(
|
||||||
DynamicMedia("ad_image", when=campaign_has_ad_image),
|
DynamicMedia("ad_image", when=campaign_has_ad_image),
|
||||||
Format("• ID: <code>{dialog_data[campaign][campaign_id]}</code>"),
|
Format("• ID: <code>{dialog_data[campaign][campaign_id]}</code>"),
|
||||||
@@ -199,12 +396,36 @@ campaigns_dialog = Dialog(
|
|||||||
Format("\t • Age from: {dialog_data[campaign][targeting][age_from]}"),
|
Format("\t • Age from: {dialog_data[campaign][targeting][age_from]}"),
|
||||||
Format("\t • Age to: {dialog_data[campaign][targeting][age_to]}"),
|
Format("\t • Age to: {dialog_data[campaign][targeting][age_to]}"),
|
||||||
Format("\t • Location: {dialog_data[campaign][targeting][location]}"),
|
Format("\t • Location: {dialog_data[campaign][targeting][location]}"),
|
||||||
Button(Const("📝 Edit campaign"), id="edit_campaign"),
|
Const("\n📊 Statistics:\n"),
|
||||||
|
Format(
|
||||||
|
"Impressions: "
|
||||||
|
"{dialog_data[campaign_statistics][impressions_count]}"
|
||||||
|
),
|
||||||
|
Format("Clicks: {dialog_data[campaign_statistics][clicks_count]}"),
|
||||||
|
Format(
|
||||||
|
"Conversion: "
|
||||||
|
"{dialog_data[campaign_statistics][impressions_count]:.2f}%"
|
||||||
|
),
|
||||||
|
Format(
|
||||||
|
"Spent on impressions: "
|
||||||
|
"{dialog_data[campaign_statistics][impressions_count]:.2f}"
|
||||||
|
),
|
||||||
|
Format(
|
||||||
|
"Spent on clicks: "
|
||||||
|
"{dialog_data[campaign_statistics][spent_clicks]:.2f}"
|
||||||
|
),
|
||||||
|
Format(
|
||||||
|
"Spent total: {dialog_data[campaign_statistics][spent_total]:.2f}"
|
||||||
|
),
|
||||||
|
Button(
|
||||||
|
Const("📝 Edit campaign"),
|
||||||
|
id="edit_campaign",
|
||||||
|
on_click=campaign_edit_on_click,
|
||||||
|
),
|
||||||
Start(
|
Start(
|
||||||
Const("⬆️ Upload image"),
|
Const("⬆️ Upload image"),
|
||||||
id="upload_ad_image",
|
id="upload_ad_image",
|
||||||
state=CampaignsDailogState.campaign_upload_ad_image,
|
state=CampaignsDailogState.campaign_upload_ad_image,
|
||||||
data=None,
|
|
||||||
when=campaign_has_not_ad_image,
|
when=campaign_has_not_ad_image,
|
||||||
),
|
),
|
||||||
Button(
|
Button(
|
||||||
@@ -218,8 +439,31 @@ campaigns_dialog = Dialog(
|
|||||||
id="delete_ad_image",
|
id="delete_ad_image",
|
||||||
on_click=delete_campaign,
|
on_click=delete_campaign,
|
||||||
),
|
),
|
||||||
Button(Const("⬅️ Back to list"), id="back", on_click=back_to_list),
|
Start(
|
||||||
|
Const("⬅️ Back to list"),
|
||||||
|
id="back",
|
||||||
|
state=CampaignsDailogState.campaigns,
|
||||||
|
),
|
||||||
state=CampaignsDailogState.campaign,
|
state=CampaignsDailogState.campaign,
|
||||||
getter=campaign_by_id,
|
getter=campaign_by_id,
|
||||||
),
|
),
|
||||||
|
Window(
|
||||||
|
Const(
|
||||||
|
"Enter new campaign info in following format "
|
||||||
|
"(each statement on new line with one enter between each):"
|
||||||
|
),
|
||||||
|
*campaign_info,
|
||||||
|
Button(
|
||||||
|
Const("⬅️ Back to campaign"),
|
||||||
|
id="back_to_campaign",
|
||||||
|
on_click=back_to_campaign,
|
||||||
|
),
|
||||||
|
TextInput(
|
||||||
|
id="campaign_update",
|
||||||
|
type_factory=check_campaign,
|
||||||
|
on_success=campaign_edit_on_success,
|
||||||
|
on_error=campaign_on_error,
|
||||||
|
),
|
||||||
|
state=CampaignsDailogState.campaign_edit,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ async def advertiser_id_on_success(
|
|||||||
state_data["advertiser_id"] = message.text
|
state_data["advertiser_id"] = message.text
|
||||||
await state.set_data(state_data)
|
await state.set_data(state_data)
|
||||||
|
|
||||||
await message.answer(f"Successfully authenticated as {message.text}")
|
await message.answer(
|
||||||
|
f"Successfully authenticated as {message.text}. Get help: /help."
|
||||||
|
)
|
||||||
await dialog_manager.mark_closed()
|
await dialog_manager.mark_closed()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
from api.schemas import CampaignCreateIn, CampaignTargeting
|
||||||
|
|
||||||
|
|
||||||
|
def campaign_from_list(fields: list[str]) -> CampaignCreateIn:
|
||||||
|
return CampaignCreateIn(
|
||||||
|
targeting=CampaignTargeting(
|
||||||
|
gender=None if fields[8] == "None" else fields[8],
|
||||||
|
age_from=None if fields[9] == "None" else fields[9],
|
||||||
|
age_to=None if fields[10] == "None" else fields[10],
|
||||||
|
location=None if fields[11] == "None" else fields[11],
|
||||||
|
),
|
||||||
|
ad_title=fields[0],
|
||||||
|
ad_text=fields[1],
|
||||||
|
impressions_limit=fields[2],
|
||||||
|
clicks_limit=fields[3],
|
||||||
|
cost_per_impression=fields[4],
|
||||||
|
cost_per_click=fields[5],
|
||||||
|
start_date=fields[6],
|
||||||
|
end_date=fields[7],
|
||||||
|
)
|
||||||
@@ -8,6 +8,7 @@ from aiogram_dialog import setup_dialogs
|
|||||||
|
|
||||||
import config
|
import config
|
||||||
from commands.campaigns import campaigns_router
|
from commands.campaigns import campaigns_router
|
||||||
|
from commands.help import help_router
|
||||||
from commands.logout import logout_router
|
from commands.logout import logout_router
|
||||||
from commands.start import start_router
|
from commands.start import start_router
|
||||||
from commands.stats import statistics_router
|
from commands.stats import statistics_router
|
||||||
@@ -43,6 +44,7 @@ async def main() -> None:
|
|||||||
dp.message.outer_middleware(AuthMiddleware())
|
dp.message.outer_middleware(AuthMiddleware())
|
||||||
|
|
||||||
dp.include_routers(
|
dp.include_routers(
|
||||||
|
help_router,
|
||||||
start_router,
|
start_router,
|
||||||
campaigns_router,
|
campaigns_router,
|
||||||
statistics_router,
|
statistics_router,
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ ignore = [
|
|||||||
"FBT001",
|
"FBT001",
|
||||||
"FBT002",
|
"FBT002",
|
||||||
"N813",
|
"N813",
|
||||||
|
"PLR2004",
|
||||||
"RUF001",
|
"RUF001",
|
||||||
"TC002",
|
"TC002",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ class CampaignsDailogState(StatesGroup):
|
|||||||
campaigns = State()
|
campaigns = State()
|
||||||
campaign = State()
|
campaign = State()
|
||||||
campaign_upload_ad_image = State()
|
campaign_upload_ad_image = State()
|
||||||
|
campaign_create = State()
|
||||||
campaign_edit = State()
|
campaign_edit = State()
|
||||||
|
|||||||
Reference in New Issue
Block a user