Skip to content

Add canvas-lms and nextdns-api Community skills#53

Open
ShadowsDistant wants to merge 2 commits intozocomputer:mainfrom
ShadowsDistant:main
Open

Add canvas-lms and nextdns-api Community skills#53
ShadowsDistant wants to merge 2 commits intozocomputer:mainfrom
ShadowsDistant:main

Conversation

@ShadowsDistant
Copy link
Copy Markdown

This PR adds two new Community skills:

canvas-lms

Fetch, create, and manage Canvas LMS data including courses, assignments, grades, enrollments, announcements, modules, discussions, quizzes, pages, files, and calendar events.

  • Full Python CLI for Canvas API
  • Environment-based auth (CANVAS_API_KEY, CANVAS_URL)
  • Complete reference documentation

nextdns-api

Query and manage NextDNS via API - full feature set including logs, analytics, profile management, blocklists, and settings.

  • Profile management (list, create, delete)
  • Settings management (security, privacy, parental controls)
  • Denylist/allowlist management
  • Blocklists and native tracker protection
  • DNS logs querying with filtering
  • Real-time logs streaming
  • Analytics (status, domains, devices, protocols, etc.)
  • Time series analytics support

Both skills follow the Agent Skills specification with proper SKILL.md frontmatter and metadata.author field.

Copilot AI review requested due to automatic review settings March 25, 2026 12:33
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds two new Community skills (“canvas-lms” and “nextdns-api”) by introducing Python CLI scripts plus SKILL.md documentation (and a Canvas API endpoints reference) so Zo can query/manage Canvas LMS and NextDNS via their respective APIs.

Changes:

  • Added a NextDNS API CLI (nextdns.py) supporting profiles, settings, lists, blocklists, logs, and analytics.
  • Added a Canvas LMS CLI (canvas_cli.py) plus a Canvas REST API endpoints reference document.
  • Added SKILL.md documentation for both skills with setup + usage examples.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
Community/nextdns-api/scripts/nextdns.py Implements the NextDNS CLI commands and API calls.
Community/nextdns-api/SKILL.md Documents NextDNS skill setup and CLI usage.
Community/canvas-lms/scripts/canvas_cli.py Implements the Canvas CLI commands, HTTP helpers, and pagination.
Community/canvas-lms/references/api_endpoints.md Provides Canvas REST API endpoint reference.
Community/canvas-lms/SKILL.md Documents Canvas skill setup, usage patterns, and examples.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Community/canvas-lms/scripts/canvas_cli.py
Comment thread Community/canvas-lms/scripts/canvas_cli.py Outdated
Comment thread Community/canvas-lms/SKILL.md
Comment thread Community/canvas-lms/SKILL.md
Comment thread Community/nextdns-api/scripts/nextdns.py
Comment thread Community/canvas-lms/scripts/canvas_cli.py Outdated
Comment on lines +36 to +41
def get(path: str, params: dict | None = None) -> list | dict:
url = api_url(path)
if params:
qs = "&".join(f"{k}={v}" for k, v in params.items() if v is not None)
if qs:
url = f"{url}?{qs}"
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Query-string construction is not URL-encoded and doesn’t support list/array params. For example --include (a list) will be rendered as a Python list string, and keys like context_codes[] won’t be encoded correctly. Use urllib.parse.urlencode(..., doseq=True) (or equivalent) to build the query string safely.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Replaced the manual string-formatting query builder with urllib.parse.urlencode(..., doseq=True). This properly encodes special characters and correctly handles list params like --include (action='append') and keys like 'context_codes[]'.

if args.student_id and args.assignment_id:
r = get(f"/courses/{args.course_id}/students/{args.student_id}/submissions/{args.assignment_id}")
elif args.student_id:
r = get(f"/courses/{args.course_id}/students/{args.student_id}/submissions", {"assignment_ids": args.assignment_id, " graded": args.graded})
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s a typo in the query params dict: the key is " graded" (leading space), so the intended graded filter won’t be sent to the API.

Suggested change
r = get(f"/courses/{args.course_id}/students/{args.student_id}/submissions", {"assignment_ids": args.assignment_id, " graded": args.graded})
r = get(f"/courses/{args.course_id}/students/{args.student_id}/submissions", {"assignment_ids": args.assignment_id, "graded": args.graded})

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Changed ' graded' (with leading space) to 'graded' in the cmd_submissions query params dict. The space was a typo that would have silently dropped the graded filter from the API request.

Comment on lines +5 to +9
import sys
import argparse
import json
import requests
from datetime import datetime, timezone
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several imports are unused (sys, and datetime/timezone). Removing them avoids lint noise and makes it clearer what the script depends on.

Suggested change
import sys
import argparse
import json
import requests
from datetime import datetime, timezone
import argparse
import json
import requests

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Removed sys, datetime, and timezone imports. sys was redundant since errors are handled via requests exceptions and SystemExit. datetime/timezone were completely unused.

Comment on lines +344 to +352
url = f"{BASE_URL}/profiles/{profile_id}/logs/download"
if not args.no_redirect:
params = {"redirect": "0"}
resp = requests.get(url, headers=get_headers(), params=params)
resp.raise_for_status()
data = resp.json()
download_url = data.get("url")
else:
download_url = url
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The --no-redirect flag behavior is inverted: when --no-redirect is NOT set, the code requests redirect=0 (no redirect) and returns a JSON URL; when --no-redirect IS set, it uses the redirecting endpoint. Swap this logic so the flag matches its help text.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Swapped the if/else logic so that: --no-redirect (flag is set) → returns redirecting URL directly for caller to follow; default (flag not set) → requests redirect=0 and returns JSON download URL. Flag behavior now matches its help text.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants