Se eliminan las herramientas que utilizaban la API de Google, toda vez la implmentacion estaba fallando. Se deja corriendo al Agente con busqueda a internet, el rag y el acceso a la hora y fecha.

This commit is contained in:
Mongar28 2024-12-26 18:17:32 -05:00
parent 283da64cd3
commit 03183457c6
9 changed files with 72 additions and 665 deletions

View File

@ -1,48 +0,0 @@
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
import os.path
import json
# Si modificas estos scopes, elimina el archivo token.json
SCOPES = [
'https://www.googleapis.com/auth/calendar', # Full access to calendar
'https://www.googleapis.com/auth/gmail.readonly', # Read Gmail profile
'https://www.googleapis.com/auth/gmail.send', # Send emails
'https://www.googleapis.com/auth/calendar.events', # Manage calendar events
'https://www.googleapis.com/auth/calendar.readonly' # Read calendar events
]
def get_google_credentials(scopes=None, credentials_file='google_credentials.json'):
"""Obtiene las credenciales de Google desde el token pre-generado.
Args:
scopes: Lista opcional de scopes de Google API
credentials_file: No se usa, mantenido por compatibilidad
Returns:
Google OAuth2 credentials
"""
# Si se proporcionan scopes, usarlos; si no, usar los predeterminados
scopes_to_use = scopes if scopes else SCOPES
# Ruta al token pre-generado
token_path = os.path.join('tokens', 'gmail_token.json')
if not os.path.exists(token_path):
raise FileNotFoundError(
f"No se encontró el archivo {token_path}. "
"Ejecuta generate_token.py fuera del Docker primero."
)
try:
# Cargar credenciales desde el token
creds = Credentials.from_authorized_user_file(token_path, scopes_to_use)
# Si el token está expirado, intentar refrescarlo
if creds.expired:
creds.refresh(Request())
return creds
except Exception as e:
raise Exception(f"Error al cargar o refrescar el token: {e}")

View File

@ -2,9 +2,6 @@ from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from datetime import datetime, timezone
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from app.rag.split_docs import load_split_docs
from app.rag.llm import load_llm_openai
@ -12,7 +9,6 @@ from app.rag.embeddings import load_embeddins
from app.rag.retriever import create_retriever
from app.rag.vectorstore import create_vectorstore
from app.rag.rag_chain import create_rag_chain
from app.google_auth import get_google_credentials
import pytz
import telebot
import os
@ -32,128 +28,6 @@ class LangChainTools:
)
return llm
def list_calendar_events(self, max_results: int = 50) -> list:
"""Lista los próximos eventos del calendario."""
try:
# Usar el sistema centralizado de autenticación
creds = get_google_credentials()
# Construir el servicio de Calendar
service = build('calendar', 'v3', credentials=creds)
# Obtener eventos próximos
now = datetime.now(timezone.utc).isoformat()
events_result = service.events().list(
calendarId='primary',
timeMin=now,
maxResults=max_results,
singleEvents=True,
orderBy='startTime'
).execute()
events = events_result.get('items', [])
if not events:
print('No se encontraron eventos próximos.')
return []
# Formatear la salida de eventos
formatted_events = []
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
formatted_events.append({
'start': start,
'summary': event['summary'],
'id': event['id'],
'link': event.get('htmlLink')
})
print(f"{start} - {event['summary']}")
return formatted_events
except Exception as e:
print(f"Error al listar eventos: {e}")
return {"error": str(e)}
def create_calendar_event(
self, title: str, start_time: datetime,
end_time: datetime, attendees: list = None) -> dict:
"""Crea un evento en el calendario."""
try:
# Usar el sistema centralizado de autenticación
creds = get_google_credentials()
# Construir el servicio de Calendar
service = build('calendar', 'v3', credentials=creds)
# Identificador del calendario principal
calendar_id = 'primary'
# Definir el evento
event = {
'summary': title,
'start': {
'dateTime': start_time.strftime('%Y-%m-%dT%H:%M:%S'),
'timeZone': 'America/Bogota',
},
'end': {
'dateTime': end_time.strftime('%Y-%m-%dT%H:%M:%S'),
'timeZone': 'America/Bogota',
},
}
# Agregar asistentes si se proporcionan
if attendees and isinstance(attendees, list) and len(attendees) > 0:
# Asegurarse de que cada asistente tenga un correo válido
attendees_list = []
for attendee in attendees:
if isinstance(attendee, str) and '@' in attendee:
attendees_list.append({'email': attendee.strip()})
if attendees_list:
event['attendees'] = attendees_list
# Enviar invitaciones por correo
event['sendUpdates'] = 'all'
# Crear el evento
event = service.events().insert(calendarId=calendar_id, body=event).execute()
print(f'Evento creado: {event.get("htmlLink")}')
return event
except Exception as e:
print(f"Error al crear el evento: {e}")
return {"error": str(e)}
def create_quick_add_event(self, quick_add_text: str) -> dict:
"""Crea un evento rápidamente usando texto en lenguaje natural."""
try:
# Usar el sistema centralizado de autenticación
creds = get_google_credentials()
# Construir el servicio de Calendar
service = build('calendar', 'v3', credentials=creds)
# Identificador del calendario principal
calendar_id = 'primary'
# Crear el evento usando quick add
event = service.events().quickAdd(
calendarId=calendar_id,
text=quick_add_text
).execute()
print(f'Evento creado: {event.get("htmlLink")}')
return event
except Exception as e:
print(f"Error al crear el evento rápido: {e}")
return {"error": str(e)}
@tool
def multiply(first_int: int, second_int: int) -> int:
"""Multiply two integers together."""
return first_int * second_int
@tool
def redact_email(topic: str) -> str:
@ -184,6 +58,7 @@ def send_message(message: str):
# Escapar caracteres especiales en Markdown
from telebot.util import escape_markdown
safe_message = escape_markdown(message)
# Enviar mensaje usando MarkdownV2
@ -197,29 +72,22 @@ def get_company_info(prompt: str) -> str:
Use this function when you need more information
about the services offered by OneCluster.
"""
file_path: str = 'onecluster_info.pdf'
file_path: str = "onecluster_info.pdf"
try:
docs_split: list = load_split_docs(file_path)
embeddings_model = load_embeddins()
llm = load_llm_openai()
# Usar el nombre corregido de la función
create_vectorstore(
docs_split,
embeddings_model,
file_path
)
create_vectorstore(docs_split, embeddings_model, file_path)
retriever = create_retriever(
embeddings_model,
persist_directory="embeddings/onecluster_info"
embeddings_model, persist_directory="embeddings/onecluster_info"
)
qa = create_rag_chain(llm, retriever)
response = qa.invoke(
{"input": prompt, "chat_history": []}
)
response = qa.invoke({"input": prompt, "chat_history": []})
return response["answer"]
except Exception as e:
@ -235,126 +103,6 @@ def get_current_date_and_time():
Returns:
str: Current date and time in Bogotá, Colombia.
"""
bogota_tz = pytz.timezone('America/Bogota')
bogota_tz = pytz.timezone("America/Bogota")
current_date_and_time = datetime.now(bogota_tz)
return current_date_and_time.strftime('%Y-%m-%d %H:%M:%S')
@tool
def list_calendar_events(max_results: int = 50) -> list:
"""Use this tool to list upcoming calendar events."""
try:
# Usar el sistema centralizado de autenticación
creds = get_google_credentials()
# Construir el servicio de Calendar
service = build('calendar', 'v3', credentials=creds)
# Obtener eventos próximos
now = datetime.now(timezone.utc).isoformat()
events_result = service.events().list(
calendarId='primary',
timeMin=now,
maxResults=max_results,
singleEvents=True,
orderBy='startTime'
).execute()
events = events_result.get('items', [])
if not events:
print('No se encontraron eventos próximos.')
return []
# Formatear la salida de eventos
formatted_events = []
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
formatted_events.append({
'start': start,
'summary': event['summary'],
'id': event['id'],
'link': event.get('htmlLink')
})
print(f"{start} - {event['summary']}")
return formatted_events
except Exception as e:
print(f"Error al listar eventos: {e}")
return {"error": str(e)}
@tool
def create_calendar_event(
title: str, start_time: datetime,
end_time: datetime, attendees: list = None) -> dict:
"""Use this tool to create an event in the calendar."""
try:
# Usar el sistema centralizado de autenticación
creds = get_google_credentials()
# Construir el servicio de Calendar
service = build('calendar', 'v3', credentials=creds)
# Identificador del calendario principal
calendar_id = 'primary'
# Definir el evento
event = {
'summary': title,
'start': {
'dateTime': start_time.strftime('%Y-%m-%dT%H:%M:%S'),
'timeZone': 'America/Bogota',
},
'end': {
'dateTime': end_time.strftime('%Y-%m-%dT%H:%M:%S'),
'timeZone': 'America/Bogota',
},
}
# Agregar asistentes si se proporcionan
if attendees and isinstance(attendees, list) and len(attendees) > 0:
# Asegurarse de que cada asistente tenga un correo válido
attendees_list = []
for attendee in attendees:
if isinstance(attendee, str) and '@' in attendee:
attendees_list.append({'email': attendee.strip()})
if attendees_list:
event['attendees'] = attendees_list
# Enviar invitaciones por correo
event['sendUpdates'] = 'all'
# Crear el evento
event = service.events().insert(calendarId=calendar_id, body=event).execute()
print(f'Evento creado: {event.get("htmlLink")}')
return event
except Exception as e:
print(f"Error al crear el evento: {e}")
return {"error": str(e)}
@tool
def create_quick_add_event(quick_add_text: str) -> dict:
"""Use this tool to create events in the calendar from natural language."""
try:
# Usar el sistema centralizado de autenticación
creds = get_google_credentials()
# Construir el servicio de Calendar
service = build('calendar', 'v3', credentials=creds)
# Identificador del calendario principal
calendar_id = 'primary'
# Crear el evento usando quick add
event = service.events().quickAdd(
calendarId=calendar_id,
text=quick_add_text
).execute()
print(f'Evento creado: {event.get("htmlLink")}')
return event
except Exception as e:
print(f"Error al crear el evento rápido: {e}")
return {"error": str(e)}
return current_date_and_time.strftime("%Y-%m-%d %H:%M:%S")

View File

@ -6,16 +6,13 @@ from langserve import add_routes
from langchain_core.prompts import ChatPromptTemplate
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_community.tools.gmail.utils import (
build_resource_service)
from langchain_community.tools.gmail.utils import build_resource_service
from langchain_community.agent_toolkits import GmailToolkit
from app.langchain_tools.agent_tools import (
redact_email,
list_calendar_events,
create_calendar_event,
get_company_info,
get_current_date_and_time
get_current_date_and_time,
)
from langgraph.graph.message import add_messages
@ -25,45 +22,24 @@ from langgraph.checkpoint.memory import MemorySaver
from typing import Annotated
from typing_extensions import TypedDict
from dotenv import load_dotenv
from app.google_auth import get_google_credentials
import os
load_dotenv()
app = FastAPI()
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.9
)
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.9)
# # Configuración de Gmail
toolkit = None
try:
token_path = os.path.join('tokens', 'gmail_token.json')
credentials = get_google_credentials(
scopes=[
"https://mail.google.com/",
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/calendar.events"
],
credentials_file="google_credentials.json"
)
api_resource = build_resource_service(credentials=credentials)
toolkit = GmailToolkit(api_resource=api_resource)
except Exception as e:
print(f"Warning: Could not initialize Gmail toolkit: {e}")
# # Crear herramientas
tools = []
if toolkit:
tools.extend(toolkit.get_tools())
search = TavilySearchResults(max_results=2)
tools.extend([
search, redact_email, list_calendar_events,
create_calendar_event, get_company_info,
get_current_date_and_time])
tools.extend(
[
search,
get_company_info,
get_current_date_and_time,
]
)
# # Definir el sistema prompt
system_prompt = ChatPromptTemplate.from_messages(
@ -72,42 +48,49 @@ system_prompt = ChatPromptTemplate.from_messages(
"system",
"Eres Mariana, el asistente virtual de OneCluster, una empresa de "
"software que ofrece soluciones personalizadas. Asume el tono de "
"J.A.R.V.I.S.: cordial, atento y con tacto en todo momento."
"J.A.R.V.I.S.: cordial, atento y con tacto en todo momento.",
),
(
"system",
"Preséntate como Mariana en el primer mensaje y pregunta el nombre "
"del usuario si no lo tienes registrado.",
),
(
"system",
"Si el usuario ya ha interactuado antes, usa su nombre sin necesidad "
"de volver a preguntar.",
),
(
"system",
"Si el primer mensaje del usuario es una solicitud, pregúntale su "
"nombre antes de responder si aún no lo conoces.",
),
(
"system",
"OneCluster es una empresa de software especializada en desarrollo a "
"medida. Solo responde a preguntas y solicitudes relacionadas con la "
"empresa y sus servicios.",
),
(
"system",
"Si necesitas información adicional sobre la empresa, usa la función "
"get_company_info.",
),
(
"system",
"Antes de enviar correos o crear eventos, muestra los detalles al "
"usuario para que los confirme antes de ejecutar la tarea.",
),
(
"system",
"Si te preguntan algo no relacionado con los servicios de OneCluster,"
" responde que solo puedes ayudar con temas relacionados con la "
"empresa y sus soluciones.",
),
("system",
"Preséntate como Mariana en el primer mensaje y pregunta el nombre "
"del usuario si no lo tienes registrado."
),
("system",
"Si el usuario ya ha interactuado antes, usa su nombre sin necesidad "
"de volver a preguntar."
),
("system",
"Si el primer mensaje del usuario es una solicitud, pregúntale su "
"nombre antes de responder si aún no lo conoces."
),
("system",
"OneCluster es una empresa de software especializada en desarrollo a "
"medida. Solo responde a preguntas y solicitudes relacionadas con la "
"empresa y sus servicios."
),
("system",
"Si necesitas información adicional sobre la empresa, usa la función "
"get_company_info."
),
("system",
"Antes de enviar correos o crear eventos, muestra los detalles al "
"usuario para que los confirme antes de ejecutar la tarea."
),
("system",
"Si te preguntan algo no relacionado con los servicios de OneCluster,"
" responde que solo puedes ayudar con temas relacionados con la "
"empresa y sus soluciones."
),
(
"system",
"Evita mencionar o hacer alusión a las herramientas que utilizas "
"internamente. Esa información es confidencial."
"internamente. Esa información es confidencial.",
),
("placeholder", "{messages}"),
]
@ -126,7 +109,7 @@ graph = create_react_agent(
tools=tools,
state_schema=State,
state_modifier=system_prompt,
checkpointer=MemorySaver()
checkpointer=MemorySaver(),
)
@ -136,11 +119,7 @@ async def redirect_root_to_docs():
# # Edit this to add the chain you want to add
add_routes(
app,
llm,
path="/openai"
)
add_routes(app, llm, path="/openai")
@app.post("/process_text")
@ -151,9 +130,8 @@ async def process_text(request: Request):
# Procesar el texto con LangChain
events = graph.stream(
{"messages": [("user", user_input)], "is_last_step": False},
config={"configurable": {
"thread_id": "thread-1", "recursion_limit": 50}},
stream_mode="updates"
config={"configurable": {"thread_id": "thread-1", "recursion_limit": 50}},
stream_mode="updates",
)
# Preparar la respuesta
@ -162,14 +140,15 @@ async def process_text(request: Request):
if "agent" in event:
response.append(event["agent"]["messages"][-1].content)
return JSONResponse(content={'response': response})
return JSONResponse(content={"response": response})
if __name__ == "__main__":
config = {"configurable": {"thread_id": "thread-1", "recursion_limit": 50}}
# Modo interactivo por defecto
import sys
if "--server" not in sys.argv:
while True:
user_input = input("User: ")
@ -177,16 +156,17 @@ if __name__ == "__main__":
print("Goodbye!")
break
events = graph.stream({
"messages": [("user", user_input)],
"is_last_step": False},
config, stream_mode="updates")
events = graph.stream(
{"messages": [("user", user_input)], "is_last_step": False},
config,
stream_mode="updates",
)
for event in events:
if "agent" in event:
print(
f"\nAsistente: {event['agent']['messages'][-1].content}\n")
print(f"\nAsistente: {event['agent']['messages'][-1].content}\n")
else:
# Modo servidor con uvicorn
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8080)

View File

@ -1,54 +0,0 @@
import unittest
from unittest.mock import patch, MagicMock
from google.oauth2.credentials import Credentials
from app.google_auth import get_google_credentials
import tempfile
import shutil
import os
class TestGoogleAuth(unittest.TestCase):
"""Test cases for Google authentication module."""
def setUp(self):
"""Set up test fixtures."""
self.mock_creds = MagicMock(spec=Credentials)
self.test_scopes = ['https://www.googleapis.com/auth/gmail.readonly']
self.test_dir = tempfile.mkdtemp()
self.token_path = os.path.join(
self.test_dir, 'tokens', 'gmail_token.json'
)
def tearDown(self):
"""Clean up after tests."""
shutil.rmtree(self.test_dir)
@patch('os.path.exists')
@patch('google.oauth2.credentials.Credentials.from_authorized_user_file')
def test_get_credentials_from_token(self, mock_creds, mock_exists):
"""Test getting credentials from existing token file."""
mock_exists.return_value = True
mock_creds.return_value = self.mock_creds
self.mock_creds.valid = True
creds = get_google_credentials(self.test_scopes)
self.assertEqual(creds, self.mock_creds)
@patch('os.path.exists')
@patch('google.oauth2.credentials.Credentials.from_authorized_user_file')
def test_refresh_expired_credentials(self, mock_creds, mock_exists):
"""Test refreshing expired credentials."""
mock_exists.return_value = True
mock_creds.return_value = self.mock_creds
self.mock_creds.valid = False
self.mock_creds.expired = True
self.mock_creds.refresh_token = True
self.mock_creds.to_json.return_value = '{"mock": "credentials"}'
creds = get_google_credentials(self.test_scopes)
self.assertTrue(self.mock_creds.refresh.called)
self.assertEqual(creds, self.mock_creds)
if __name__ == '__main__':
unittest.main()

View File

View File

@ -1,27 +0,0 @@
from fastapi.testclient import TestClient
from app.server import app # Asegúrate de importar tu aplicación FastAPI
# Crea un cliente de prueba
client = TestClient(app)
def test_process_text():
# Define el texto de entrada
input_text = {"text": "Hola, ¿cómo estás?"}
# Realiza una solicitud POST al endpoint
response = client.post("/process_text", json=input_text)
# Verifica que la respuesta tenga un código de estado 200
assert response.status_code == 200
# Verifica que la respuesta contenga la clave 'response'
assert 'response' in response.json()
# Verifica que la respuesta sea una lista
assert isinstance(response.json()['response'], list)
# Aquí puedes agregar más verificaciones
# según lo que esperas en la respuesta
# Por ejemplo, verificar que la lista no esté vacía
assert len(response.json()['response']) > 0

View File

@ -1,176 +0,0 @@
# Sistema de Autenticación con Google APIs
Este documento describe el sistema de autenticación centralizado implementado para interactuar con las APIs de Google (Calendar, Gmail, etc.) en proyectos de Python.
## Descripción General
El sistema utiliza OAuth 2.0 para autenticar y autorizar el acceso a las APIs de Google. La implementación está centralizada en un módulo `google_auth.py` que maneja:
- Gestión de credenciales
- Autenticación inicial
- Renovación automática de tokens
- Manejo de múltiples alcances (scopes)
## Archivos Necesarios
1. **google_credentials.json**: Credenciales de OAuth 2.0 del proyecto de Google Cloud
- Contiene client_id, client_secret y otras configuraciones
- Se obtiene desde Google Cloud Console
- **¡IMPORTANTE!** No compartir ni commitear este archivo
2. **token.json**: Almacena los tokens de acceso y actualización
- Se genera automáticamente en el primer uso
- Se actualiza automáticamente cuando los tokens expiran
- **¡IMPORTANTE!** No compartir ni commitear este archivo
3. **.gitignore**: Debe incluir los archivos sensibles
```
google_credentials.json
token.json
```
## Configuración Inicial
1. Crear un proyecto en Google Cloud Console
2. Habilitar las APIs necesarias (Calendar, Gmail, etc.)
3. Configurar la pantalla de consentimiento OAuth
4. Crear credenciales OAuth 2.0
5. Descargar el archivo de credenciales como `google_credentials.json`
## Implementación
### 1. Módulo Central de Autenticación (google_auth.py)
```python
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
import os
def get_google_credentials(
scopes=None,
token_file='token.json',
credentials_file='google_credentials.json'
):
"""
Obtiene o refresca las credenciales de Google.
Args:
scopes: Lista de scopes requeridos
token_file: Ruta al archivo de token
credentials_file: Ruta al archivo de credenciales
Returns:
Credentials: Objeto de credenciales de Google
"""
creds = None
if os.path.exists(token_file):
creds = Credentials.from_authorized_user_file(token_file, scopes)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
credentials_file, scopes)
creds = flow.run_local_server(port=0)
with open(token_file, 'w') as token:
token.write(creds.to_json())
return creds
```
### 2. Uso en Funciones Específicas
```python
from googleapiclient.discovery import build
from google_auth import get_google_credentials
def create_calendar_event(title, start_time, end_time):
# Definir los scopes necesarios
SCOPES = ['https://www.googleapis.com/auth/calendar']
# Obtener credenciales
creds = get_google_credentials(SCOPES)
# Construir el servicio
service = build('calendar', 'v3', credentials=creds)
# Usar el servicio
event = {
'summary': title,
'start': {'dateTime': start_time},
'end': {'dateTime': end_time},
}
return service.events().insert(calendarId='primary', body=event).execute()
```
## Scopes Comunes
- Calendar: `https://www.googleapis.com/auth/calendar`
- Gmail (lectura): `https://www.googleapis.com/auth/gmail.readonly`
- Gmail (envío): `https://www.googleapis.com/auth/gmail.send`
- Gmail (completo): `https://mail.google.com/`
## Mejores Prácticas
1. **Seguridad**:
- Nunca commitear archivos de credenciales
- Usar variables de entorno para configuraciones sensibles
- Mantener los tokens seguros
2. **Manejo de Errores**:
- Implementar reintentos para errores temporales
- Manejar errores de autenticación específicamente
- Logging apropiado de errores
3. **Gestión de Tokens**:
- Implementar rotación de tokens
- Monitorear la expiración de tokens
- Manejar revocación de acceso
4. **Organización del Código**:
- Mantener la autenticación centralizada
- Separar la lógica de negocio de la autenticación
- Usar clases para encapsular funcionalidad relacionada
## Integración con Frameworks
### LangChain
```python
from langchain.tools import Tool
from google_auth import get_google_credentials
def create_google_tools():
creds = get_google_credentials(SCOPES)
service = build('calendar', 'v3', credentials=creds)
return [
Tool(
name="Calendar",
func=lambda x: service.events().list().execute(),
description="Access Google Calendar"
)
]
```
## Solución de Problemas
1. **Token Expirado**:
- El sistema renovará automáticamente los tokens expirados
- Si falla, eliminar `token.json` y reautenticar
2. **Errores de Scope**:
- Verificar que todos los scopes necesarios estén incluidos
- Eliminar `token.json` si se agregan nuevos scopes
3. **Errores de Credenciales**:
- Verificar que `google_credentials.json` sea válido
- Confirmar que las APIs estén habilitadas en Google Cloud Console
## Referencias
- [Google OAuth 2.0 Documentation](https://developers.google.com/identity/protocols/oauth2)
- [Google Calendar API](https://developers.google.com/calendar)
- [Google Gmail API](https://developers.google.com/gmail/api)

View File

@ -1 +0,0 @@
{"installed":{"client_id":"123598969297-2686q5bm5ajnbk96dt9u3ajgc3o7441c.apps.googleusercontent.com","project_id":"onecluster-443003","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"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","redirect_uris":["http://localhost"]}}

View File

@ -1,15 +0,0 @@
{
"token": "ya29.a0AeDClZA5S3XiL0XEA5h7hXqNU1zh-QaK_S2dMmf6QYNiwyewk7dsMd-QzvTpObkFnKT8dJDRW-dNCR9NhmJFD28CgsgIWEBHt4gPjdoVWVLg6YI9CQi3SdAxL2spnbBVugk5rlOOhMSj6O318hM3yCynUN_hQ-szL3nvoTz_aCgYKAbMSARASFQHGX2MinJv72L34S299HgpKR5VM6A0175",
"refresh_token": "1//05v6o5t9wAP18CgYIARAAGAUSNwF-L9IrYkA9pEhJx2HtRbKL9rR-HV8N-3H5xMWCdHCf1WgyCMGVa3HLulUoDP6ILW84wOw3zJM",
"token_uri": "https://oauth2.googleapis.com/token",
"client_id": "123598969297-2686q5bm5ajnbk96dt9u3ajgc3o7441c.apps.googleusercontent.com",
"client_secret": "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",
"scopes": [
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.send",
"https://www.googleapis.com/auth/calendar.events",
"https://www.googleapis.com/auth/calendar.readonly"
],
"expiry": "2024-11-27T07:44:50.818190"
}