Metadata-Version: 2.4
Name: portulong
Version: 1.0.0
Summary: Linguagem de programação para bots Discord em Português de Portugal
Project-URL: Homepage, https://github.com/portulong/portulong
Project-URL: Documentation, https://github.com/portulong/portulong#readme
Project-URL: Repository, https://github.com/portulong/portulong
Author: PortuLang
License-Expression: MIT
Keywords: bot,discord,language,portuguese,transpiler
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Software Development :: Compilers
Requires-Python: >=3.8
Requires-Dist: discord-py>=2.0.0
Description-Content-Type: text/markdown

# PortuLang - Bot Discord 100% em Português de Portugal

Uma linguagem de programação que permite criar bots Discord escrevendo código 100% em Português de Portugal.

## Índice

1. [Instalação](#instalação)
2. [Primeiro Bot](#primeiro-bot)
3. [Executar Código](#executar-código)
4. [Palavras-Chave](#palavras-chave)
5. [Estruturas de Controlo](#estruturas-de-controlo)
6. [Funções Built-in](#funções-built-in)
7. [Classes do Discord](#classes-do-discord)
8. [Eventos](#eventos)
9. [Comandos](#comandos)
10. [Propriedades dos Objetos](#propriedades-dos-objetos)
11. [Exceções](#exceções)
12. [Exemplos Completos](#exemplos-completos)

---

## Instalação

### Requisitos

- Python 3.8 ou superior
- pip

### Instalar via pip (quando publicado no PyPI)

```bash
pip install portulong
```

### Instalar do GitHub (recomendado agora)

```bash
pip install git+https://github.com/portulong/portulong.git
```

### Ou instalar a partir do código fonte

```bash
git clone https://github.com/portulong/portulong.git
cd portulong
pip install .
```

### Verificar instalação

```bash
portulong --help
```

### Configurar o token do bot

Cria um ficheiro `.ptg` (ver exemplo abaixo) e substitui `"O_TEU_TOKEN_AQUI"` pelo teu token de bot Discord.

> **IMPORTANTE**: Nunca partilhes o teu token publicamente!

---

## Primeiro Bot

Cria um ficheiro chamado `meu_bot.ptg`:

```ptg
# O meu primeiro bot Discord em Português

importar discord
de discord.ext importar comandos

# Configuração do bot
intentos = discord.Intencoes.default()
intentos.conteudo_mensagem = verdadeiro
cliente = comandos.Bot(comando_prefixo="!", intents=intentos)

# Evento: quando o bot liga
@cliente.evento
assincrono ao_ligar():
    presente("Bot ligado como {cliente.user}")

# Comando: !ola
@cliente.comando()
assincrono ola(ctx):
    assunto("Olá {ctx.autor.mencao}! Como estás?")

# Iniciar o bot
cliente.executar("O_TEU_TOKEN_AQUI")
```

---

## Executar Código

### Usar o terminal

```bash
portulong meu_bot.ptg
```

### Compilar para Python

```bash
portulong -c meu_bot.ptg
```

Isto gera um ficheiro `meu_bot.py` que pode ser executado normalmente.

### Mostrar código processado

```bash
portulong -m meu_bot.ptg
```

Mostra o código PT-PT processado antes de executar.

### Usar a extensão VS Code

1. Abre um ficheiro `.ptg` no VS Code
2. Carrega no botão "Run" na barra de ferramentas
3. O bot executa automaticamente

---

## Palavras-Chave

Todas as palavras-chave do PortuLang são traduzidas automaticamente para Python.

### Estruturas de Controlo

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `se` | `if` | Condicional |
| `senao_se` | `elif` | Condicional alternativa |
| `senao` | `else` | Caso contrário |
| `para` | `for` | Ciclo para |
| `enquanto` | `while` | Ciclo enquanto |
| `parar` | `break` | Sair do ciclo |
| `continuar` | `continue` | Próxima iteração |
| `definir` | `def` | Definir função |
| `retornar` | `return` | Retornar valor |
| `classificar` | `class` | Definir classe |
| `tentar` | `try` | Tentar operação |
| `excetar` | `except` | Apanhar erro |
| `finalmente` | `finally` | Bloco sempre executado |
| `levantar` | `raise` | Lançar exceção |
| `assegurar` | `assert` | Verificar afirmação |
| `assincrono` | `async` | Função assíncrona |
| `esperar` | `await` | Esperar operação |
| `passar` | `pass` | Sem operação |
| `global` | `global` | Variável global |
| `nao_local` | `nonlocal` | Variável não-local |
| `rendimento` | `yield` | Gerador |

### Valores

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `verdadeiro` | `True` | Verdadeiro |
| `falso` | `False` | Falso |
| `nulo` | `None` | Nulo |

### Operadores Lógicos

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `e` | `and` | E lógico |
| `ou` | `or` | Ou lógico |
| `nao` | `not` | Negação |
| `em` | `in` | Pertence a |

### Operadores Compostos

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `e_igual` | `==` | Igual a |
| `nao_e_igual` | `!=` | Diferente de |
| `maior_ou_igual` | `>=` | Maior ou igual |
| `menor_ou_igual` | `<=` | Menor ou igual |

### Tipos de Dados

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `inteiro` | `int` | Número inteiro |
| `cadeia` | `str` | Texto |
| `decimal` | `float` | Número decimal |
| `boleano` | `bool` | Booleano |
| `lista` | `list` | Lista |
| `dicionario` | `dict` | Dicionário |
| `conjunto` | `set` | Conjunto |
| `tuplo` | `tuple` | Tuplo |
| `nenhum` | `None` | Nulo |
| `qualquer` | `object` | Qualquer objeto |

---

## Funções Built-in

### Saída

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `presente(...)` | `print(...)` | Imprimir texto |
| `apresentar(...)` | `print(...)` | Imprimir texto |

### Input

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `receber_input(...)` | `input(...)` | Ler input |

### Informações

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `tipo(x)` | `type(x)` | Tipo do objeto |
| `comprimento(x)` | `len(x)` | Comprimento |
| `tamanho(x)` | `len(x)` | Comprimento |
| `existe(x, y)` | `isinstance(x, y)` | Verificar tipo |

### Listas

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `listar(x)` | `list(x)` | Criar lista |
| `adicionar(lista, x)` | `lista.append(x)` | Adicionar item |
| `inserir(lista, i, x)` | `lista.insert(i, x)` | Inserir na posição |
| `eliminar(x)` | `del x` | Eliminar |
| `copiar(x)` | `x.copy()` | Copiar |
| `organizar(x)` | `x.sort()` | Ordenar |
| `ordenar(x)` | `x.sort()` | Ordenar |
| `inverter(x)` | `x.reverse()` | Inverter |
| `soma(x)` | `sum(x)` | Somar |
| `minimo(x)` | `min(x)` | Mínimo |
| `maximo(x)` | `max(x)` | Máximo |
| `emparelhar(x, y)` | `zip(x, y)` | Emparelhar |
| `mapear(f, x)` | `map(f, x)` | Mapear |
| `filtrar(f, x)` | `filter(f, x)` | Filtrar |
| `enumerar(x)` | `enumerate(x)` | Enumerar |

### Dicionários

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `dicionario(x)` | `dict(x)` | Criar dicionário |
| `obter(d, k)` | `d.get(k)` | Obter valor |
| `atualizar(d, x)` | `d.update(x)` | Atualizar |
| `chaves(d)` | `d.keys()` | Chaves |
| `valores(d)` | `d.values()` | Valores |
| `itens(d)` | `d.items()` | Itens |

### Texto

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `substituir(t, a, b)` | `t.replace(a, b)` | Substituir |
| `dividir(t, s)` | `t.split(s)` | Dividir |
| `juntar(l, s)` | `s.join(l)` | Juntar |
| `maiusculas(t)` | `t.upper()` | Maiúsculas |
| `minusculas(t)` | `t.lower()` | Minúsculas |
| `aparar(t)` | `t.strip()` | Aparar |
| `procurar(t, s)` | `t.find(s)` | Procurar |
| `comeca_com(t, s)` | `t.startswith(s)` | Começa com |
| `termina_com(t, s)` | `t.endswith(s)` | Termina com |

### Matemática

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `absoluto(x)` | `abs(x)` | Valor absoluto |
| `arredondar(x)` | `round(x)` | Arredondar |
| `intervalo(x)` | `range(x)` | Intervalo |

### Ficheiros

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `abrir(c)` | `open(c)` | Abrir ficheiro |
| `fechar(f)` | `f.close()` | Fechar ficheiro |
| `ler(f)` | `f.read()` | Ler ficheiro |
| `escrever(f, t)` | `f.write(t)` | Escrever ficheiro |

### Outros

| PortuLang | Python | Descrição |
|-----------|--------|-----------|
| `existe(x, y)` | `isinstance(x, y)` | Verificar tipo |
| `global` | `global` | Variável global |
| `nao_local` | `nonlocal` | Variável não-local |
| `rendimento` | `yield` | Gerador |

---

## Classes do Discord

### Bot

```ptg
importar discord
de discord.ext importar comandos

intentos = discord.Intencoes.default()
intentos.conteudo_mensagem = verdadeiro
cliente = comandos.Bot(comando_prefixo="!", intents=intentos)
```

**Propriedades:**
- `cliente.servidores` - Lista de servidores
- `cliente.user` - Utilizador do bot
- `cliente.latencia` - Latência WebSocket
- `cliente.intents` - Intenções configuradas
- `cliente.todos_canais` - Todos os canais
- `cliente.todos_membros` - Todos os membros

**Métodos:**
- `cliente.executar(token)` - Iniciar o bot
- `cliente.obter_servidor(id)` - Obter servidor por ID
- `cliente.obter_canal(id)` - Obter canal por ID
- `cliente.obter_utilizador(id)` - Obter utilizador por ID
- `cliente.criar_dm(utilizador)` - Criar canal DM
- `cliente.alterar_presenca(...)` - Alterar presença

---

### Intencoes

```ptg
intentos = discord.Intencoes.default()
intentos.conteudo_mensagem = verdadeiro
```

**Propriedades:**
- `intentos.conteudo_mensagem` - Conteúdo de mensagens
- `intentos.servidores` - Eventos de servidores
- `intentos.membros` - Eventos de membros
- `intentos.mensagens` - Eventos de mensagens
- `intentos.restricoes` - Eventos de banimentos
- `intentos.presencas` - Eventos de presença
- `intentos.reaccoes` - Eventos de reações
- `intentos.escrever` - Eventos de digitação
- `intentos.estados_voz` - Eventos de voz
- `intentos.convites` - Eventos de convites
- `intentos.emojis_stickers` - Eventos de emojis/stickers
- `intentos.integracoes` - Eventos de integrações
- `intentos.eventos_agendados` - Eventos agendados
- `intentos.sondagens` - Eventos de sondagens

---

### Servidor (Guild)

```ptg
servidor = ctx.servidor
```

**Propriedades:**
- `servidor.nome` - Nome do servidor
- `servidor.id` - ID do servidor
- `servidor.dono` - Dono do servidor
- `servidor.proprietario_id` - ID do dono
- `servidor.membros` - Lista de membros
- `servidor.canais` - Todos os canais
- `servidor.canais_texto` - Canais de texto
- `servidor.canais_voz` - Canais de voz
- `servidor.categorias` - Categorias
- `servidor.cargos` - Todos os cargos
- `servidor.cargo_predefinido` - Cargo @everyone
- `servidor.criado_em` - Data de criação
- `servidor.contagem_membros` - Número de membros
- `servidor.descricao` - Descrição
- `servidor.icone` - Ícone
- `servidor.banner` - Banner
- `servidor.emojis` - Emojis
- `servidor.stickers` - Stickers
- `servidor.canal_sistema` - Canal de sistema
- `servidor.canal_regras` - Canal de regras
- `servidor.nivel_verificacao` - Nível de verificação
- `servidor.nivel_premium` - Nível de Nitro
- `servidor.contagem_boosts` - Número de boosts
- `servidor.grande` - É grande (>250 membros)
- `servidor.indisponivel` - Está indisponível
- `servidor.limite_ficheiro` - Limite de upload
- `servidor.limite_emoji` - Limite de emojis
- `servidor.limite_bitrate` - Limite de bitrate
- `servidor.me` - O bot neste servidor
- `servidor.eventos_agendados` - Eventos agendados

**Métodos:**
- `servidor.criar_canal(nome, ...)` - Criar canal de texto
- `servidor.criar_canal_voz(nome, ...)` - Criar canal de voz
- `servidor.criar_canal_estagio(nome, ...)` - Criar canal de estágio
- `servidor.criar_categoria(nome, ...)` - Criar categoria
- `servidor.criar_forum(nome, ...)` - Criar forum
- `servidor.criar_cargo(nome, ...)` - Criar cargo
- `servidor.banir(utilizador, ...)` - Banir utilizador
- `servidor.desbanir(utilizador, ...)` - Desbanir utilizador
- `servidor.expulsar(utilizador, ...)` - Expulsar utilizador
- `servidor.obter_membro(id)` - Obter membro por ID
- `servidor.obter_canal(id)` - Obter canal por ID
- `servidor.obter_cargo(id)` - Obter cargo por ID
- `servidor.obter_emoji(id)` - Obter emoji por ID
- `servidor.buscar_membro(id)` - Buscar membro da API
- `servidor.buscar_canal(id)` - Buscar canal da API
- `servidor.eliminar()` - Sair do servidor
- `servidor.editar(...)` - Editar servidor

---

### Membro

```ptg
membro = ctx.autor
```

**Propriedades:**
- `membro.nome` - Nome de utilizador
- `membro.nome_display` - Nome exibido
- `membro.nome_global` - Nome global
- `membro.alcunha` - Alcunha no servidor
- `membro.id` - ID do utilizador
- `membro.mencao` - Menção (@user)
- `membro.bot` - É um bot
- `membro.servidor` - Servidor pertencente
- `membro.cargo` - Cargo mais alto
- `membro.cargos` - Todos os cargos
- `membro.cor` - Cor do cargo mais alto
- `membro.permissoes` - Permissões no servidor
- `membro.voz` - Estado de voz
- `membro.estado` - Estado (online/offline/etc)
- `membro.atividades` - Atividades atuais
- `membro.entrou_em` - Data de entrada
- `membro.premium_desde` - Data do boost
- `membro.pendente` - Pendente de verificação
- `membro.avatar` - Avatar
- `membro.avatar_servidor` - Avatar do servidor
- `membro.avatar_display` - Avatar exibido
- `membro.banner` - Banner
- `membro.criado_em` - Data de criação da conta
- `membro.bandeiras` - Bandeiras públicas

**Métodos:**
- `membro.banir(...)` - Banir
- `membro.desbanir(...)` - Desbanir
- `membro.expulsar(...)` - Expulsar
- `membro.silenciar(duracao, ...)` - Silenciar (timeout)
- `membro.desilenciar()` - Remover silenciamento
- `membro.adicionar_cargos(*cargos)` - Adicionar cargos
- `membro.remover_cargos(*cargos)` - Remover cargos
- `membro.editar(...)` - Editar membro
- `membro.mover_para(canal)` - Mover para canal de voz
- `membro.criar_dm()` - Criar canal DM
- `membro.enviar(texto)` - Enviar mensagem DM
- `membro.obter_cargo(id)` - Obter cargo por ID
- `membro.mencionado_em(mensagem)` - Verificar menção
- `membro.esta_no_mobile()` - Está no mobile
- `membro.esta_silenciado()` - Está silenciado

---

### Canal (TextChannel)

```ptg
canal = ctx.canal
```

**Propriedades:**
- `canal.nome` - Nome do canal
- `canal.id` - ID do canal
- `canal.tipo` - Tipo do canal
- `canal.servidor` - Servidor pertencente
- `canal.topico` - Tópico
- `canal.posicao` - Posição
- `canal.nsfw` - É NSFW
- `canal.url` - URL para saltar

**Métodos:**
- `canal.enviar(texto, ...)` - Enviar mensagem
- `canal.apagar(...)` - Apagar canal
- `canal.limpar(limite, ...)` - Limpar mensagens
- `canal.criar_thread(nome, ...)` - Criar thread
- `canal.criar_convite(...)` - Criar convite
- `canal.obter_mensagem(id)` - Obter mensagem
- `canal.historico(...)` - Histórico de mensagens
- `canal.bloquear(alvo, permissoes)` - Bloquear permissões
- `canal.definir_permissoes(alvo, overwrite)` - Definir permissões

---

### CanalVoz (VoiceChannel)

```ptg
canal_voz = membro.voz.canal
```

**Propriedades:**
- `canal_voz.nome` - Nome
- `canal_voz.id` - ID
- `canal_voz.membros` - Membros conectados
- `canal_voz.limite_utilizadores` - Limite de utilizadores
- `canal_voz.bitrate` - Bitrate

**Métodos:**
- `canal_voz.conectar(...)` - Ligar ao canal
- `canal_voz.criar_convite(...)` - Criar convite

---

### Cargo (Role)

```ptg
cargo = servidor.criar_cargo(nome="Moderador")
```

**Propriedades:**
- `cargo.nome` - Nome
- `cargo.id` - ID
- `cargo.cor` - Cor
- `cargo.posicao` - Posição
- `cargo.permissoes` - Permissões
- `cargo.separado` - É exibido separadamente
- `cargo.mencionavel` - Pode ser mencionado
- `cargo.mencao` - Menção (@cargo)
- `cargo.gerido` - É gerido por integração
- `cargo.integrado` - É de integração
- `cargo.gerido_bot` - É gerido por bot
- `cargo.premio` - É cargo de premium
- `cargo.e_predefinido` - É @everyone
- `cargo.criado_em` - Data de criação
- `cargo.membros` - Membros com este cargo
- `cargo.icone` - Ícone
- `cargo.emoji_unicode` - Emoji unicode
- `cargo.etiquetas` - Etiquetas

**Métodos:**
- `cargo.apagar(...)` - Apagar cargo
- `cargo.editar(...)` - Editar cargo
- `cargo.mover(...)` - Mover na hierarquia
- `cargo.pode_atribuir()` - Bot pode atribuir

---

### Mensagem (Message)

```ptg
mensagem = ctx.mensagem
```

**Propriedades:**
- `mensagem.conteudo` - Conteúdo do texto
- `mensagem.conteudo_limpo` - Conteúdo limpo (menções resolvidas)
- `mensagem.autor` - Autor da mensagem
- `mensagem.canal` - Canal onde foi enviada
- `mensagem.servidor` - Servidor
- `mensagem.id` - ID da mensagem
- `mensagem.mencoes` - Utilizadores mencionados
- `mensagem.mencoes_cargos` - Cargos mencionados
- `mensagem.mencoes_canais` - Canais mencionados
- `mensagem.anexos` - Ficheiros anexados
- `mensagem.embeds` - Embeds
- `mensagem.reacoes` - Reações
- `mensagem.fixada` - Está fixada
- `mensagem.tipo` - Tipo da mensagem
- `mensagem.criada_em` - Data de criação
- `mensagem.editada_em` - Data de edição
- `mensagem.url` - URL para saltar
- `mensagem.referencia` - Referência (resposta)
- `mensagem.diz_todos` - Mencionou @everyone
- `mensagem.stickers` - Stickers

**Métodos:**
- `mensagem.responder(texto, ...)` - Responder
- `mensagem.reagir(emoji)` - Adicionar reação
- `mensagem.remover_reacao(emoji, membro)` - Remover reação
- `mensagem.limpar_reacoes()` - Limpar todas as reações
- `mensagem.apagar(...)` - Apagar mensagem
- `mensagem.editar(...)` - Editar mensagem
- `mensagem.fixar()` - Fixar mensagem
- `mensagem.desfixar()` - Desfixar mensagem
- `mensagem.publicar()` - Publicar em canal de anúncios
- `mensagem.criar_thread(nome, ...)` - Criar thread

---

### Embed

```ptg
embed = discord.Embed(
    titulo="Título",
    descricao="Descrição",
    cor=0x00ff00
)
```

**Propriedades:**
- `embed.titulo` - Título
- `embed.descricao` - Descrição
- `embed.cor` - Cor
- `embed.url` - URL
- `embed.timestamp` - Timestamp
- `embed.campos` - Lista de campos

**Métodos:**
- `embed.definir_titulo(t)` - Definir título
- `embed.definir_descricao(d)` - Definir descrição
- `embed.definir_cor(c)` - Definir cor
- `embed.definir_url(u)` - Definir URL
- `embed.definir_timestamp(t)` - Definir timestamp
- `embed.adicionar_campo(nome, valor, em_linha)` - Adicionar campo
- `embed.inserir_campo(idx, nome, valor, em_linha)` - Inserir campo
- `embed.definir_campo(idx, nome, valor, em_linha)` - Editar campo
- `embed.remover_campo(idx)` - Remover campo
- `embed.limpar_campos()` - Limpar campos
- `embed.definir_autor(nome, url, icone_url)` - Definir autor
- `embed.remover_autor()` - Remover autor
- `embed.definir_rodape(texto, icone_url)` - Definir rodapé
- `embed.definir_imagem(url)` - Definir imagem
- `embed.definir_miniatura(url)` - Definir miniatura
- `embed.definir_video(url)` - Definir vídeo

---

### Permissoes

```ptg
se ctx.autor.permissoes.administrador:
    assunto("É administrador!")
```

**Propriedades:**
- `permissoes.administrador` - Administrador
- `permissoes.banir_membros` - Banir membros
- `permissoes.expulsar_membros` - Expulsar membros
- `permissoes.gerir_canais` - Gerir canais
- `permissoes.gerir_servidor` - Gerir servidor
- `permissoes.enviar_mensagens` - Enviar mensagens
- `permissoes.ler_historial` - Ler histórico
- `permissoes.ver_canal` - Ver canal
- `permissoes.ligar` - Ligar a voz
- `permissoes.falar` - Falar em voz
- `permissoes.gerir_cargos` - Gerir cargos
- `permissoes.gerir_mensagens` - Gerir mensagens
- `permissoes.mencionar_todos` - Mencionar @everyone
- `permissoes.anexar_ficheiros` - Anexar ficheiros
- `permissoes.integrar_links` - Integrar links
- `permissoes.adicionar_reaccoes` - Adicionar reações
- `permissoes.usar_emojis_externos` - Usar emojis externos
- `permissoes.ver_registo_auditoria` - Ver registo de auditoria
- `permissoes.moderar_membros` - Moderar membros (timeout)
- `permissoes.mutar_membros` - Mutar membros
- `permissoes.ensurdecer_membros` - Ensurdecer membros
- `permissoes.mover_membros` - Mover membros
- `permissoes.usar_vad` - Usar deteção de voz
- `permissoes.mudar_alcunha` - Mudar alcunha
- `permissoes.gerir_alcunhas` - Gerir alcunhas
- `permissoes.gerir_whorks` - Gerir webhooks
- `permissoes.gerir_emojis_stickers` - Gerir emojis/stickers
- `permissoes.usar_comandos_app` - Usar comandos de app
- `permissoes.gerir_eventos` - Gerir eventos
- `permissoes.gerir_threads` - Gerir threads
- `permissoes.criar_threads_publicas` - Criar threads públicas
- `permissoes.criar_threads_privadas` - Criar threads privadas
- `permissoes.enviar_mensagens_threads` - Enviar em threads
- `permissoes.usar_stickers_externos` - Usar stickers externos
- `permissoes.actividades_embutidas` - Atividades embutidas
- `permissoes.enviar_sondagens` - Enviar sondagens
- `permissoes.usar_soundboard` - Usar soundboard
- `permissoes.enviar_mensagens_voz` - Enviar mensagens de voz
- `permissoes.criar_eventos` - Criar eventos

---

### Ficheiro (File)

```ptg
ficheiro = discord.ficheiro("caminho/imagem.png")
canal.enviar("Aqui está!", ficheiro=ficheiro)
```

**Construtor:**
- `discord.ficheiro(caminho, nome=None, spoiler=False, descricao=None)`

**Parâmetros:**
- `caminho` - Caminho do ficheiro, bytes, ou objeto file-like
- `nome` - Nome exibido no Discord
- `spoiler` - É spoiler
- `descricao` - Descrição (texto alternativo)

---

### Cor (Colour)

```ptg
cor_verde = discord.cor(0x00ff00)
cor_aleatoria = discord.cor_aleatoria()
```

**Métodos estáticos (via discord.py):**
- `discord.Colour.default()` - Preto
- `discord.Colour.teal()` - Verde-azulado
- `discord.Colour.green()` - Verde
- `discord.Colour.blue()` - Azul
- `discord.Colour.purple()` - Roxo
- `discord.Colour.gold()` - Dourado
- `discord.Colour.orange()` - Laranja
- `discord.Colour.red()` - Vermelho
- `discord.Colour.greyple()` - Cinza claro
- `discord.Colour.random()` - Aleatório

---

## Eventos

Os eventos são decorados com `@cliente.evento` e a função deve ter o nome do evento em PT-PT.

### Conexão

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_ligar` | Bot ligou | - |
| `ao_retomar` | Sessão retomada | - |
| `ao_ligar_servidor` | Conectado ao gateway | - |
| `ao_desligar_servidor` | Desconectado | - |

### Mensagens

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_receber_mensagem` | Mensagem recebida | `mensagem` |
| `ao_mensagem_editada` | Mensagem editada | `antes, depois` |
| `ao_mensagem_apagada` | Mensagem apagada | `mensagem` |
| `ao_mensagens_apagadas` | Mensagens apagadas em bloco | `mensagens` |

### Membros

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_membro_entrar` | Membro entrou | `membro` |
| `ao_membro_sair` | Membro saiu | `membro` |
| `ao_membro_atualizar` | Membro atualizado | `antes, depois` |
| `ao_banir_membro` | Membro banido | `servidor, utilizador` |
| `ao_desbanir_membro` | Membro desbanido | `servidor, utilizador` |
| `ao_utilizador_atualizar` | Utilizador atualizado | `antes, depois` |
| `ao_presenca_atualizar` | Presença atualizada | `antes, depois` |

### Canais

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_canal_servidor_criar` | Canal criado | `canal` |
| `ao_canal_servidor_apagar` | Canal apagado | `canal` |
| `ao_canal_servidor_atualizar` | Canal atualizado | `antes, depois` |
| `ao_pins_servidor_atualizar` | Pins atualizados | `canal, data` |

### Servidores

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_servidor_entrar` | Bot entrou no servidor | `servidor` |
| `ao_servidor_sair` | Bot saiu do servidor | `servidor` |
| `ao_servidor_atualizar` | Servidor atualizado | `antes, depois` |
| `ao_servidor_disponivel` | Servidor disponível | `servidor` |
| `ao_servidor_indisponivel` | Servidor indisponível | `servidor` |
| `ao_emojis_servidor_atualizar` | Emojis atualizados | `servidor, antes, depois` |
| `ao_stickers_servidor_atualizar` | Stickers atualizados | `servidor, antes, depois` |

### Cargos

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_cargo_criar` | Cargo criado | `cargo` |
| `ao_cargo_apagar` | Cargo apagado | `cargo` |
| `ao_cargo_atualizar` | Cargo atualizado | `antes, depois` |

### Reações

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_reacao_adicionar` | Reação adicionada | `reacao, utilizador` |
| `ao_reacao_remover` | Reação removida | `reacao, utilizador` |
| `ao_reacoes_limpar` | Reações limpas | `mensagem, reacoes` |
| `ao_emoji_reacao_limpar` | Emoji de reação limpo | `reacao` |

### Voz

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_estado_voz_atualizar` | Estado de voz mudou | `membro, antes, depois` |

### Threads

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_thread_criar` | Thread criada | `thread` |
| `ao_thread_juntar` | Bot juntou-se a thread | `thread` |
| `ao_thread_atualizar` | Thread atualizada | `antes, depois` |
| `ao_thread_remover` | Bot removido de thread | `thread` |
| `ao_thread_apagar` | Thread apagada | `thread` |

### Interações

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_interacao` | Interação recebida | `interacao` |
| `ao_comando_app_concluir` | Comando app concluído | `interacao, comando` |

### Eventos Agendados

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_evento_agendado_criar` | Evento criado | `evento` |
| `ao_evento_agendado_apagar` | Evento apagado | `evento` |
| `ao_evento_agendado_atualizar` | Evento atualizado | `antes, depois` |
| `ao_evento_agendado_utilizador_entrar` | Utilizador entrou | `evento, utilizador` |
| `ao_evento_agendado_utilizador_sair` | Utilizador saiu | `evento, utilizador` |

### Outros

| Evento | Descrição | Parâmetros |
|--------|-----------|------------|
| `ao_escrever` | Alguém começou a escrever | `canal, utilizador, quando` |
| `ao_convite_criar` | Convite criado | `convite` |
| `ao_convite_apagar` | Convite apagado | `convite` |
| `ao_erro` | Erro num evento | `evento, *args` |

---

## Comandos

Os comandos são decorados com `@cliente.comando()`.

### Comando Simples

```ptg
@cliente.comando()
assincrono ola(ctx):
    assunto("Olá!")
```

Invocação: `!ola`

### Comando com Nome Personalizado

```ptg
@cliente.comando(nome="obter_info")
assincrono info(ctx):
    assunto("Informação do bot")
```

Invocação: `!obter_info`

### Comando com Argumentos

```ptg
@cliente.comando()
assincrono dizer(ctx, *, texto: cadeia):
    assunto("Dizes-te: {texto}")
```

Invocação: `!dizer Olá mundo`

### Comando com Argumento Obrigatório e Opcional

```ptg
@cliente.comando()
assincrono banir(ctx, membro: discord.Member, *, razao: cadeia = "Sem razão"):
    se nao ctx.autor.permissoes.banir_membro:
        assunto("Não tens permissão!")
        retornar
    esperar membro.banir(razao=razao)
    assunto("{membro.nome} foi banido!")
```

Invocação: `!banir @Utilizador Razão`

### Contexto (ctx)

O objeto `ctx` contém:

- `ctx.autor` - Autor do comando
- `ctx.canal` - Canal onde foi invocado
- `ctx.servidor` - Servidor
- `ctx.mensagem` - Mensagem original
- `ctx.argumentos` - Argumentos
- `ctx.prefixo` - Prefixo usado
- `ctx.nome_comando` - Nome do comando

---

## Propriedades dos Objetos

### Interpolação de Strings

O PortuLang suporta interpolação de strings com `{variavel}`:

```ptg
nome = "João"
assunto("Olá {nome}!")
assunto("Tens {len(servidor.membros)} membros!")
```

Isto é automaticamente convertido para f-strings em Python.

---

## Exceções

| Exceção | Descrição |
|---------|-----------|
| `ExcecaoDiscord` | Exceção geral do Discord |
| `ComandoNaoEncontrado` | Comando não encontrado |
| `ArgumentosInsuficientes` | Argumentos em falta |
| `PermissaoNegada` | Utilizador sem permissão |
| `BotSemPermissoes` | Bot sem permissão |
| `FalhaLogin` | Token inválido |
| `ExcecaoHTTP` | Erro HTTP |
| `NaoEncontrado` | Recurso não encontrado |
| `Proibido` | Acesso proibido |

### Tratamento de Erros

```ptg
@cliente.evento
assincrono ao_comando_erro(ctx, erro):
    se tipo(erro) == ComandoNaoEncontrado:
        assunto("Comando não encontrado!")
    senao_se tipo(erro) == ArgumentosInsuficientes:
        assunto("Faltam argumentos!")
    senao:
        assunto("Erro: {erro}")
```

---

## Exemplos Completos

### Bot de Boas-Vindas

```ptg
importar discord
de discord.ext importar comandos

intentos = discord.Intencoes.default()
intentos.membros = verdadeiro
intentos.conteudo_mensagem = verdadeiro
cliente = comandos.Bot(comando_prefixo="!", intents=intentos)

@cliente.evento
assincrono ao_ligar():
    presente("Bot ligado!")

@cliente.evento
assincrono ao_membro_entrar(membro):
    canal = membro.servidor.canal_sistema
    se canal:
        esperar canal.enviar("Bem-vindo {membro.mencao}!")

cliente.executar("TOKEN")
```

### Bot de Moderação

```ptg
importar discord
de discord.ext importar comandos

intentos = discord.Intencoes.default()
intentos.conteudo_mensagem = verdadeiro
cliente = comandos.Bot(comando_prefixo="!", intents=intentos)

@cliente.comando()
assincrono banir(ctx, membro: discord.Member, *, razao: cadeia = "Sem razão"):
    se nao ctx.autor.permissoes.banir_membro:
        assunto("Não tens permissão para banir!")
        retornar
    tentar:
        esperar membro.banir(razao=razao)
        assunto("{membro.nome} foi banido! Razão: {razao}")
    excetar discord.ExcecaoDiscord as erro:
        assunto("Erro ao banir: {erro}")

@cliente.comando()
assuncrono expulsar(ctx, membro: discord.Member, *, razao: cadeia = "Sem razão"):
    se nao ctx.autor.permissoes.expulsar_membros:
        assunto("Não tens permissão para expulsar!")
        retornar
    tentar:
        esperar membro.expulsar(razao=razao)
        assunto("{membro.nome} foi expulso!")
    excetar discord.ExcecaoDiscord as erro:
        assunto("Erro ao expulsar: {erro}")

@cliente.comando()
assincrono limpar(ctx, quantidade: inteiro = 5):
    se nao ctx.autor.permissoes.gerir_mensagens:
        assunto("Não tens permissão!")
        retornar
    eliminados = esperar ctx.canal.limpar(limite=quantidade)
    assunto("Apaguei {len(eliminados)} mensagens!")

cliente.executar("TOKEN")
```

### Bot de Embed

```ptg
importar discord
de discord.ext importar comandos

intentos = discord.Intencoes.default()
intentos.conteudo_mensagem = verdadeiro
cliente = comandos.Bot(comando_prefixo="!", intents=intentos)

@cliente.comando()
assincrono embed(ctx):
    embed = discord.Embed(
        titulo="Exemplo de Embed",
        descricao="Isto é um embed de exemplo",
        cor=0x00ff00
    )
    embed.adicionar_campo(nome="Campo 1", valor="Valor 1", em_linha=verdadeiro)
    embed.adicionar_campo(nome="Campo 2", valor="Valor 2", em_linha=verdadeiro)
    embed.definir_rodape(texto="Rodapé do embed")
    embed.definir_imagem(url="https://exemplo.com/imagem.png")
    esperar ctx.canal.enviar(embed=embed)

@cliente.comando()
assincrono embed_personalizado(ctx, titulo: cadeia, *, descricao: cadeia):
    embed = discord.Embed(titulo=titulo, descricao=descricao, cor=discord.cor_aleatoria())
    esperar ctx.canal.enviar(embed=embed)

cliente.executar("TOKEN")
```

### Bot de Informação do Servidor

```ptg
importar discord
de discord.ext importar comandos

intentos = discord.Intencoes.default()
intentos.conteudo_mensagem = verdadeiro
cliente = comandos.Bot(comando_prefixo="!", intents=intentos)

@cliente.comando()
assincrono servidor(ctx):
    s = ctx.servidor
    embed = discord.Embed(
        titulo=s.nome,
        cor=0x3498db
    )
    embed.adicionar_campo(nome="ID", valor=str(s.id), em_linha=verdadeiro)
    embed.adicionar_campo(nome="Dono", valor=str(s.dono), em_linha=verdadeiro)
    embed.adicionar_campo(nome="Membros", valor=str(s.contagem_membros), em_linha=verdadeiro)
    embed.adicionar_campo(nome="Canais", valor=str(len(s.canais)), em_linha=verdadeiro)
    embed.adicionar_campo(nome="Cargos", valor=str(len(s.cargos)), em_linha=verdadeiro)
    embed.adicionar_campo(nome="Criado em", valor=str(s.criado_em), em_linha=verdadeiro)
    se s.icone:
        embed.definir_miniatura(url=s.icone.url)
    se s.banner:
        embed.definir_imagem(url=s.banner.url)
    esperar ctx.canal.enviar(embed=embed)

cliente.executar("TOKEN")
```

### Bot com Botões (Views)

```ptg
importar discord
de discord.ext importar comandos

intentos = discord.Intencoes.default()
intentos.conteudo_mensagem = verdadeiro
cliente = comandos.Bot(comando_prefixo="!", intents=intentos)

@cliente.comando()
assincrono botao(ctx):
    embed = discord.Embed(titulo="Clica no botão!")
    vista = discord.ui.View()
    botao = discord.ui.Button(label="Clica!", style=discord.ButtonStyle.primary)
    
    assincrono callback(interacao):
        esperar interacao.responder(content="Clicaste!", ephemeral=verdadeiro)
    
    botao.callback = callback
    vista.adicionar_item(botao)
    esperar ctx.canal.enviar(embed=embed, view=vista)

cliente.executar("TOKEN")
```

---

## Estrutura do Projeto

```
portulong/
├── pyproject.toml              # Configuração do pacote
├── README.md                   # Documentação
├── publicar.sh                 # Script para publicar no PyPI
├── src/
│   └── portulong/              # Pacote principal
│       ├── __init__.py
│       ├── cli.py              # Ponto de entrada CLI
│       ├── transpilador.py     # Transpilador PT-PT → Python
│       ├── discord_pt.py       # Módulo de compatibilidade
│       └── core_keywords.py    # Palavras-chave
├── main.ptg                    # Bot principal (exemplo)
└── extensao-ptg/               # Extensão VS Code
```

---

## FAQ

### O bot não liga?

1. Verifica se o token está correto
2. Verifica se o discord.py está instalado: `pip show discord.py`
3. Verifica se o venv está ativo: `source .venv/bin/activate`

### O comando não é encontrado?

1. Verifica se o prefixo está correto (por padrão `!`)
2. Verifica se o nome da função corresponde ao nome do comando
3. Verifica se o decorador `@cliente.comando()` está correto

### O evento não dispara?

1. Verifica se tens as Intenções corretas
2. Para `ao_membro_entrar`, precisas de `intentos.membros = verdadeiro`
3. Para `ao_receber_mensagem`, precisas de `intentos.conteudo_mensagem = verdadeiro`

### Erro "ModuleNotFoundError"?

1. Verifica se o venv está ativo
2. Reinstala discord.py: `uv pip install discord.py`

---

## Licença

MIT License
