ConnectWise PSA skills and tools under ops/connectwise (hub-sanitized 2026-11-01: client intel, internal workflow/credential/member/company ids, archived-page apiToken/sessionId stripped to placeholders)
This commit is contained in:
commit
9c96092034
36 files changed
+310875
No files matched your search
@@ -0,0 +1,69 @@
|
||||
import base64
|
||||
import requests
|
||||
import pandas as pd
|
||||
from datetime import datetime
|
||||
import sys
|
||||
|
||||
def generate_report(owner_id, company_id, public_key, private_key, client_id):
|
||||
base_url = 'https://cw.vistanetinc.com/v4_6_release/apis/3.0'
|
||||
|
||||
# Auth Format: Basic base64(companyidpublickey:privatekey)
|
||||
auth_str = f"{company_id}{public_key}:{private_key}"
|
||||
encoded_auth = base64.b64encode(auth_str.encode()).decode()
|
||||
headers = {
|
||||
'Authorization': f'Basic {encoded_auth}',
|
||||
'clientId': client_id,
|
||||
'Company-Id': company_id,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
conditions = f"closedFlag=false and owner/identifier='{owner_id}'"
|
||||
url = f"{base_url}/service/tickets?conditions={conditions}"
|
||||
|
||||
response = requests.get(url, headers=headers)
|
||||
if response.status_code != 200:
|
||||
print(f"Error: API request failed with status {response.status_code}: {response.text}")
|
||||
sys.exit(1)
|
||||
|
||||
tickets = response.json()
|
||||
if not tickets:
|
||||
return f"No open tickets found for {owner_id}."
|
||||
|
||||
data = []
|
||||
for t in tickets:
|
||||
status_name = t.get('status', {}).get('name', 'Unknown') if isinstance(t.get('status'), dict) else t.get('status', 'Unknown')
|
||||
if status_name.startswith('>'):
|
||||
continue
|
||||
|
||||
data.append({
|
||||
'Ticket #': t.get('id', 'N/A'),
|
||||
'Company': t.get('company', {}).get('name', 'N/A') if isinstance(t.get('company'), dict) else t.get('company', 'N/A'),
|
||||
'Owner': t.get('owner', {}).get('identifier', 'N/A') if isinstance(t.get('owner'), dict) else t.get('owner', 'N/A'),
|
||||
'Status': status_name,
|
||||
'Summary': t.get('summary', 'No Summary'),
|
||||
'Date Entered': t.get('dateEntered', 'N/A')
|
||||
})
|
||||
|
||||
if not data:
|
||||
return f"No open tickets found for {owner_id} (excluding completed statuses)."
|
||||
|
||||
df = pd.DataFrame(data).sort_values(by='Date Entered', ascending=False)
|
||||
|
||||
return f'''
|
||||
<div style="font-family: Arial, Helvetica, sans-serif; color: #333;">
|
||||
<h3 style="color: #0056b3;">Open Tickets Report: {owner_id}</h3>
|
||||
<p>Generated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
|
||||
{df.to_html(index=False, border=1, justify='left')}
|
||||
</div>
|
||||
'''
|
||||
|
||||
if __name__ == "__main__":
|
||||
# This script is intended to be used by the agent providing credentials from a secret store
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: generate_open_tickets.py <owner_id>")
|
||||
sys.exit(1)
|
||||
|
||||
# Note: Actual credentials should be passed via env or a wrapper;
|
||||
# the agent should read from ~/.secrets/connectwise
|
||||
print("Error: This script requires a wrapper to inject credentials from ~/.secrets/connectwise")
|
||||
sys.exit(1)
|
||||
Reference in new issue
Block a user