Initial backup 2026-02-17
This commit is contained in:
105
skills/apple-calendar/scripts/cal-create.sh
Normal file
105
skills/apple-calendar/scripts/cal-create.sh
Normal file
@@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
# Create a new calendar event
|
||||
# Usage: cal-create.sh <calendar> <summary> <start_date> <end_date> [location] [description] [allday] [recurrence]
|
||||
# Date format: "YYYY-MM-DD HH:MM" or "YYYY-MM-DD" for all-day events
|
||||
# Recurrence format: iCalendar RRULE (e.g., "FREQ=WEEKLY;COUNT=4" or "FREQ=DAILY;UNTIL=20260201")
|
||||
# Examples:
|
||||
# cal-create.sh Personal "Meeting" "2026-01-15 10:00" "2026-01-15 11:00"
|
||||
# cal-create.sh Personal "Vacation" "2026-02-01" "2026-02-05" "" "Beach trip" true
|
||||
# cal-create.sh Personal "Weekly Standup" "2026-01-20 09:00" "2026-01-20 09:30" "Zoom" "" false "FREQ=WEEKLY;COUNT=10"
|
||||
|
||||
CALENDAR="${1:-}"
|
||||
SUMMARY="${2:-}"
|
||||
START_DATE="${3:-}"
|
||||
END_DATE="${4:-}"
|
||||
LOCATION="${5:-}"
|
||||
DESCRIPTION="${6:-}"
|
||||
ALL_DAY="${7:-false}"
|
||||
RECURRENCE="${8:-}"
|
||||
|
||||
if [ -z "$CALENDAR" ] || [ -z "$SUMMARY" ] || [ -z "$START_DATE" ] || [ -z "$END_DATE" ]; then
|
||||
echo "Usage: cal-create.sh <calendar> <summary> <start_date> <end_date> [location] [description] [allday] [recurrence]"
|
||||
echo "Date format: 'YYYY-MM-DD HH:MM' or 'YYYY-MM-DD' for all-day"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
osascript - "$CALENDAR" "$SUMMARY" "$START_DATE" "$END_DATE" "$LOCATION" "$DESCRIPTION" "$ALL_DAY" "$RECURRENCE" <<'EOF'
|
||||
on splitString(theString, theDelimiter)
|
||||
set oldDelimiters to AppleScript's text item delimiters
|
||||
set AppleScript's text item delimiters to theDelimiter
|
||||
set theArray to every text item of theString
|
||||
set AppleScript's text item delimiters to oldDelimiters
|
||||
return theArray
|
||||
end splitString
|
||||
|
||||
on parseDate(dateStr)
|
||||
set dateParts to my splitString(dateStr, " ")
|
||||
set ymdParts to my splitString(item 1 of dateParts, "-")
|
||||
|
||||
set theDate to current date
|
||||
set year of theDate to (item 1 of ymdParts) as integer
|
||||
set month of theDate to (item 2 of ymdParts) as integer
|
||||
set day of theDate to (item 3 of ymdParts) as integer
|
||||
|
||||
if (count of dateParts) > 1 then
|
||||
set timeParts to my splitString(item 2 of dateParts, ":")
|
||||
set hours of theDate to (item 1 of timeParts) as integer
|
||||
set minutes of theDate to (item 2 of timeParts) as integer
|
||||
set seconds of theDate to 0
|
||||
else
|
||||
set hours of theDate to 0
|
||||
set minutes of theDate to 0
|
||||
set seconds of theDate to 0
|
||||
end if
|
||||
|
||||
return theDate
|
||||
end parseDate
|
||||
|
||||
on run argv
|
||||
set calendarName to item 1 of argv as string
|
||||
set eventSummary to item 2 of argv as string
|
||||
set startDateStr to item 3 of argv as string
|
||||
set endDateStr to item 4 of argv as string
|
||||
set eventLocation to item 5 of argv as string
|
||||
set eventDescription to item 6 of argv as string
|
||||
set isAllDay to item 7 of argv as string
|
||||
set eventRecurrence to item 8 of argv as string
|
||||
|
||||
set startDate to my parseDate(startDateStr)
|
||||
set endDate to my parseDate(endDateStr)
|
||||
|
||||
tell application "Calendar"
|
||||
try
|
||||
set cal to calendar calendarName
|
||||
on error
|
||||
return "Error: Calendar '" & calendarName & "' not found"
|
||||
end try
|
||||
|
||||
if not (writable of cal) then
|
||||
return "Error: Calendar '" & calendarName & "' is read-only"
|
||||
end if
|
||||
|
||||
set eventProps to {summary:eventSummary, start date:startDate, end date:endDate}
|
||||
|
||||
if isAllDay is "true" then
|
||||
set eventProps to eventProps & {allday event:true}
|
||||
end if
|
||||
|
||||
set newEvent to make new event at end of events of cal with properties eventProps
|
||||
|
||||
if eventLocation is not "" then
|
||||
set location of newEvent to eventLocation
|
||||
end if
|
||||
|
||||
if eventDescription is not "" then
|
||||
set description of newEvent to eventDescription
|
||||
end if
|
||||
|
||||
if eventRecurrence is not "" then
|
||||
set recurrence of newEvent to eventRecurrence
|
||||
end if
|
||||
|
||||
return "Created event: " & (uid of newEvent)
|
||||
end tell
|
||||
end run
|
||||
EOF
|
||||
50
skills/apple-calendar/scripts/cal-delete.sh
Normal file
50
skills/apple-calendar/scripts/cal-delete.sh
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
# Delete a calendar event by UID
|
||||
# Usage: cal-delete.sh <event-uid> [calendar_name]
|
||||
# If calendar not specified, searches all calendars
|
||||
|
||||
EVENT_UID="${1:-}"
|
||||
CALENDAR_NAME="${2:-}"
|
||||
|
||||
if [ -z "$EVENT_UID" ]; then
|
||||
echo "Usage: cal-delete.sh <event-uid> [calendar_name]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
osascript - "$EVENT_UID" "$CALENDAR_NAME" <<'EOF'
|
||||
on run argv
|
||||
set eventUID to item 1 of argv as string
|
||||
set calendarName to item 2 of argv as string
|
||||
|
||||
tell application "Calendar"
|
||||
if calendarName is not "" then
|
||||
try
|
||||
set cals to {calendar calendarName}
|
||||
on error
|
||||
return "Error: Calendar '" & calendarName & "' not found"
|
||||
end try
|
||||
else
|
||||
set cals to calendars
|
||||
end if
|
||||
|
||||
repeat with cal in cals
|
||||
try
|
||||
set matchingEvents to (every event of cal whose uid is eventUID)
|
||||
if (count of matchingEvents) > 0 then
|
||||
set e to item 1 of matchingEvents
|
||||
set eventName to summary of e
|
||||
|
||||
if not (writable of cal) then
|
||||
return "Error: Calendar '" & (name of cal) & "' is read-only"
|
||||
end if
|
||||
|
||||
delete e
|
||||
return "Deleted event: " & eventName & " (" & eventUID & ")"
|
||||
end if
|
||||
end try
|
||||
end repeat
|
||||
|
||||
return "Error: Event with UID '" & eventUID & "' not found"
|
||||
end tell
|
||||
end run
|
||||
EOF
|
||||
66
skills/apple-calendar/scripts/cal-events.sh
Normal file
66
skills/apple-calendar/scripts/cal-events.sh
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
# List events in a date range
|
||||
# Usage: cal-events.sh [days_ahead] [calendar_name]
|
||||
# Examples:
|
||||
# cal-events.sh # Today's events from all calendars
|
||||
# cal-events.sh 7 # Next 7 days from all calendars
|
||||
# cal-events.sh 7 Personal # Next 7 days from Personal calendar only
|
||||
|
||||
DAYS_AHEAD="${1:-0}"
|
||||
CALENDAR_NAME="${2:-}"
|
||||
|
||||
osascript - "$DAYS_AHEAD" "$CALENDAR_NAME" <<'EOF'
|
||||
on run argv
|
||||
set daysAhead to item 1 of argv as integer
|
||||
set calendarName to item 2 of argv as string
|
||||
|
||||
tell application "Calendar"
|
||||
set today to current date
|
||||
set startOfDay to today - (time of today)
|
||||
|
||||
if daysAhead = 0 then
|
||||
set endDate to startOfDay + (24 * 60 * 60)
|
||||
else
|
||||
set endDate to startOfDay + ((daysAhead + 1) * 24 * 60 * 60)
|
||||
end if
|
||||
|
||||
set results to {}
|
||||
|
||||
if calendarName is not "" then
|
||||
try
|
||||
set cals to {calendar calendarName}
|
||||
on error
|
||||
return "Error: Calendar '" & calendarName & "' not found"
|
||||
end try
|
||||
else
|
||||
set cals to calendars
|
||||
end if
|
||||
|
||||
repeat with cal in cals
|
||||
try
|
||||
set calEvents to (every event of cal whose start date ≥ startOfDay and start date < endDate)
|
||||
repeat with e in calEvents
|
||||
set eventStart to start date of e
|
||||
set eventEnd to end date of e
|
||||
set isAllDay to allday event of e
|
||||
set eventLoc to location of e
|
||||
if eventLoc is missing value then set eventLoc to ""
|
||||
|
||||
set eventLine to (uid of e) & " | " & (summary of e) & " | " & (eventStart as string) & " | " & (eventEnd as string) & " | " & (isAllDay as string) & " | " & eventLoc & " | " & (name of cal)
|
||||
set end of results to eventLine
|
||||
end repeat
|
||||
end try
|
||||
end repeat
|
||||
|
||||
if (count of results) = 0 then
|
||||
return "No events found"
|
||||
end if
|
||||
|
||||
set output to ""
|
||||
repeat with r in results
|
||||
set output to output & r & linefeed
|
||||
end repeat
|
||||
return output
|
||||
end tell
|
||||
end run
|
||||
EOF
|
||||
22
skills/apple-calendar/scripts/cal-list.sh
Normal file
22
skills/apple-calendar/scripts/cal-list.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
# List all calendars with their properties
|
||||
# Usage: cal-list.sh
|
||||
|
||||
osascript <<'EOF'
|
||||
tell application "Calendar"
|
||||
set calNames to name of every calendar
|
||||
set calWritable to writable of every calendar
|
||||
set output to ""
|
||||
repeat with i from 1 to count of calNames
|
||||
set calName to item i of calNames
|
||||
set isWritable to item i of calWritable
|
||||
if isWritable then
|
||||
set writeStatus to "writable"
|
||||
else
|
||||
set writeStatus to "read-only"
|
||||
end if
|
||||
set output to output & calName & " | " & writeStatus & linefeed
|
||||
end repeat
|
||||
return output
|
||||
end tell
|
||||
EOF
|
||||
69
skills/apple-calendar/scripts/cal-read.sh
Normal file
69
skills/apple-calendar/scripts/cal-read.sh
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
# Read a single event by UID
|
||||
# Usage: cal-read.sh <event-uid> [calendar_name]
|
||||
# If calendar not specified, searches all calendars
|
||||
|
||||
EVENT_UID="${1:-}"
|
||||
CALENDAR_NAME="${2:-}"
|
||||
|
||||
if [ -z "$EVENT_UID" ]; then
|
||||
echo "Usage: cal-read.sh <event-uid> [calendar_name]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
osascript - "$EVENT_UID" "$CALENDAR_NAME" <<'EOF'
|
||||
on run argv
|
||||
set eventUID to item 1 of argv as string
|
||||
set calendarName to item 2 of argv as string
|
||||
|
||||
tell application "Calendar"
|
||||
if calendarName is not "" then
|
||||
try
|
||||
set cals to {calendar calendarName}
|
||||
on error
|
||||
return "Error: Calendar '" & calendarName & "' not found"
|
||||
end try
|
||||
else
|
||||
set cals to calendars
|
||||
end if
|
||||
|
||||
repeat with cal in cals
|
||||
try
|
||||
set matchingEvents to (every event of cal whose uid is eventUID)
|
||||
if (count of matchingEvents) > 0 then
|
||||
set e to item 1 of matchingEvents
|
||||
|
||||
set eventSummary to summary of e
|
||||
set eventStart to start date of e
|
||||
set eventEnd to end date of e
|
||||
set isAllDay to allday event of e
|
||||
set eventLoc to location of e
|
||||
set eventDesc to description of e
|
||||
set eventURL to url of e
|
||||
set eventRecur to recurrence of e
|
||||
|
||||
if eventLoc is missing value then set eventLoc to ""
|
||||
if eventDesc is missing value then set eventDesc to ""
|
||||
if eventURL is missing value then set eventURL to ""
|
||||
if eventRecur is missing value then set eventRecur to ""
|
||||
|
||||
set output to "UID: " & eventUID & linefeed
|
||||
set output to output & "Calendar: " & (name of cal) & linefeed
|
||||
set output to output & "Summary: " & eventSummary & linefeed
|
||||
set output to output & "Start: " & (eventStart as string) & linefeed
|
||||
set output to output & "End: " & (eventEnd as string) & linefeed
|
||||
set output to output & "All Day: " & (isAllDay as string) & linefeed
|
||||
set output to output & "Location: " & eventLoc & linefeed
|
||||
set output to output & "Description: " & eventDesc & linefeed
|
||||
set output to output & "URL: " & eventURL & linefeed
|
||||
set output to output & "Recurrence: " & eventRecur
|
||||
|
||||
return output
|
||||
end if
|
||||
end try
|
||||
end repeat
|
||||
|
||||
return "Error: Event with UID '" & eventUID & "' not found"
|
||||
end tell
|
||||
end run
|
||||
EOF
|
||||
100
skills/apple-calendar/scripts/cal-search.sh
Normal file
100
skills/apple-calendar/scripts/cal-search.sh
Normal file
@@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
# Search events by text (summary, location, or description)
|
||||
# Usage: cal-search.sh <query> [days_ahead] [calendar_name]
|
||||
# Examples:
|
||||
# cal-search.sh "meeting" # Search all calendars, next 30 days
|
||||
# cal-search.sh "dentist" 90 # Search next 90 days
|
||||
# cal-search.sh "standup" 14 Work # Search Work calendar, next 14 days
|
||||
|
||||
QUERY="${1:-}"
|
||||
DAYS_AHEAD="${2:-30}"
|
||||
CALENDAR_NAME="${3:-}"
|
||||
|
||||
if [ -z "$QUERY" ]; then
|
||||
echo "Usage: cal-search.sh <query> [days_ahead] [calendar_name]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
osascript - "$QUERY" "$DAYS_AHEAD" "$CALENDAR_NAME" <<'EOF'
|
||||
on run argv
|
||||
set searchQuery to item 1 of argv as string
|
||||
set daysAhead to item 2 of argv as integer
|
||||
set calendarName to item 3 of argv as string
|
||||
|
||||
tell application "Calendar"
|
||||
set today to current date
|
||||
set startOfDay to today - (time of today)
|
||||
set endDate to startOfDay + (daysAhead * 24 * 60 * 60)
|
||||
|
||||
set results to {}
|
||||
|
||||
if calendarName is not "" then
|
||||
try
|
||||
set cals to {calendar calendarName}
|
||||
on error
|
||||
return "Error: Calendar '" & calendarName & "' not found"
|
||||
end try
|
||||
else
|
||||
set cals to calendars
|
||||
end if
|
||||
|
||||
repeat with cal in cals
|
||||
try
|
||||
set calEvents to (every event of cal whose start date ≥ startOfDay and start date < endDate)
|
||||
repeat with e in calEvents
|
||||
set eventSummary to summary of e
|
||||
set eventLoc to location of e
|
||||
set eventDesc to description of e
|
||||
|
||||
if eventLoc is missing value then set eventLoc to ""
|
||||
if eventDesc is missing value then set eventDesc to ""
|
||||
|
||||
-- Case-insensitive search in summary, location, or description
|
||||
set lowerQuery to my toLowerCase(searchQuery)
|
||||
set matchFound to false
|
||||
|
||||
if my toLowerCase(eventSummary) contains lowerQuery then
|
||||
set matchFound to true
|
||||
else if my toLowerCase(eventLoc) contains lowerQuery then
|
||||
set matchFound to true
|
||||
else if my toLowerCase(eventDesc) contains lowerQuery then
|
||||
set matchFound to true
|
||||
end if
|
||||
|
||||
if matchFound then
|
||||
set eventStart to start date of e
|
||||
set isAllDay to allday event of e
|
||||
set eventLine to (uid of e) & " | " & eventSummary & " | " & (eventStart as string) & " | " & (isAllDay as string) & " | " & eventLoc & " | " & (name of cal)
|
||||
set end of results to eventLine
|
||||
end if
|
||||
end repeat
|
||||
end try
|
||||
end repeat
|
||||
|
||||
if (count of results) = 0 then
|
||||
return "No events found matching: " & searchQuery
|
||||
end if
|
||||
|
||||
set output to ""
|
||||
repeat with r in results
|
||||
set output to output & r & linefeed
|
||||
end repeat
|
||||
return output
|
||||
end tell
|
||||
end run
|
||||
|
||||
on toLowerCase(theString)
|
||||
set lowercaseChars to "abcdefghijklmnopqrstuvwxyz"
|
||||
set uppercaseChars to "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
set resultString to ""
|
||||
repeat with c in theString
|
||||
set charIndex to offset of c in uppercaseChars
|
||||
if charIndex > 0 then
|
||||
set resultString to resultString & character charIndex of lowercaseChars
|
||||
else
|
||||
set resultString to resultString & c
|
||||
end if
|
||||
end repeat
|
||||
return resultString
|
||||
end toLowerCase
|
||||
EOF
|
||||
148
skills/apple-calendar/scripts/cal-update.sh
Normal file
148
skills/apple-calendar/scripts/cal-update.sh
Normal file
@@ -0,0 +1,148 @@
|
||||
#!/bin/bash
|
||||
# Update an existing calendar event
|
||||
# Usage: cal-update.sh <event-uid> [--calendar <name>] [--summary <text>] [--start <date>] [--end <date>] [--location <text>] [--description <text>] [--allday <true/false>] [--recurrence <rrule>]
|
||||
# Date format: "YYYY-MM-DD HH:MM" or "YYYY-MM-DD" for all-day events
|
||||
# Examples:
|
||||
# cal-update.sh ABC123 --summary "Updated Meeting"
|
||||
# cal-update.sh ABC123 --calendar Personal --start "2026-01-16 14:00" --end "2026-01-16 15:00"
|
||||
# cal-update.sh ABC123 --location "Room 101" --description "Bring laptop"
|
||||
|
||||
EVENT_UID=""
|
||||
CALENDAR_NAME=""
|
||||
SUMMARY=""
|
||||
START_DATE=""
|
||||
END_DATE=""
|
||||
LOCATION=""
|
||||
DESCRIPTION=""
|
||||
ALL_DAY=""
|
||||
RECURRENCE=""
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--calendar) CALENDAR_NAME="$2"; shift 2 ;;
|
||||
--summary) SUMMARY="$2"; shift 2 ;;
|
||||
--start) START_DATE="$2"; shift 2 ;;
|
||||
--end) END_DATE="$2"; shift 2 ;;
|
||||
--location) LOCATION="$2"; shift 2 ;;
|
||||
--description) DESCRIPTION="$2"; shift 2 ;;
|
||||
--allday) ALL_DAY="$2"; shift 2 ;;
|
||||
--recurrence) RECURRENCE="$2"; shift 2 ;;
|
||||
*)
|
||||
if [ -z "$EVENT_UID" ]; then
|
||||
EVENT_UID="$1"
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$EVENT_UID" ]; then
|
||||
echo "Usage: cal-update.sh <event-uid> [--calendar <name>] [--summary <text>] [--start <date>] [--end <date>] [--location <text>] [--description <text>] [--allday <true/false>] [--recurrence <rrule>]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
osascript - "$EVENT_UID" "$CALENDAR_NAME" "$SUMMARY" "$START_DATE" "$END_DATE" "$LOCATION" "$DESCRIPTION" "$ALL_DAY" "$RECURRENCE" <<'EOF'
|
||||
on splitString(theString, theDelimiter)
|
||||
set oldDelimiters to AppleScript's text item delimiters
|
||||
set AppleScript's text item delimiters to theDelimiter
|
||||
set theArray to every text item of theString
|
||||
set AppleScript's text item delimiters to oldDelimiters
|
||||
return theArray
|
||||
end splitString
|
||||
|
||||
on parseDate(dateStr)
|
||||
if dateStr is "" then return missing value
|
||||
set dateParts to my splitString(dateStr, " ")
|
||||
set ymdParts to my splitString(item 1 of dateParts, "-")
|
||||
|
||||
set theDate to current date
|
||||
set year of theDate to (item 1 of ymdParts) as integer
|
||||
set month of theDate to (item 2 of ymdParts) as integer
|
||||
set day of theDate to (item 3 of ymdParts) as integer
|
||||
|
||||
if (count of dateParts) > 1 then
|
||||
set timeParts to my splitString(item 2 of dateParts, ":")
|
||||
set hours of theDate to (item 1 of timeParts) as integer
|
||||
set minutes of theDate to (item 2 of timeParts) as integer
|
||||
set seconds of theDate to 0
|
||||
else
|
||||
set hours of theDate to 0
|
||||
set minutes of theDate to 0
|
||||
set seconds of theDate to 0
|
||||
end if
|
||||
|
||||
return theDate
|
||||
end parseDate
|
||||
|
||||
on run argv
|
||||
set eventUID to item 1 of argv as string
|
||||
set calendarName to item 2 of argv as string
|
||||
set newSummary to item 3 of argv as string
|
||||
set newStartStr to item 4 of argv as string
|
||||
set newEndStr to item 5 of argv as string
|
||||
set newLocation to item 6 of argv as string
|
||||
set newDescription to item 7 of argv as string
|
||||
set newAllDay to item 8 of argv as string
|
||||
set newRecurrence to item 9 of argv as string
|
||||
|
||||
tell application "Calendar"
|
||||
if calendarName is not "" then
|
||||
try
|
||||
set cals to {calendar calendarName}
|
||||
on error
|
||||
return "Error: Calendar '" & calendarName & "' not found"
|
||||
end try
|
||||
else
|
||||
set cals to calendars
|
||||
end if
|
||||
|
||||
repeat with cal in cals
|
||||
try
|
||||
set matchingEvents to (every event of cal whose uid is eventUID)
|
||||
if (count of matchingEvents) > 0 then
|
||||
set e to item 1 of matchingEvents
|
||||
|
||||
if not (writable of cal) then
|
||||
return "Error: Calendar '" & (name of cal) & "' is read-only"
|
||||
end if
|
||||
|
||||
if newSummary is not "" then
|
||||
set summary of e to newSummary
|
||||
end if
|
||||
|
||||
if newStartStr is not "" then
|
||||
set start date of e to my parseDate(newStartStr)
|
||||
end if
|
||||
|
||||
if newEndStr is not "" then
|
||||
set end date of e to my parseDate(newEndStr)
|
||||
end if
|
||||
|
||||
if newLocation is not "" then
|
||||
set location of e to newLocation
|
||||
end if
|
||||
|
||||
if newDescription is not "" then
|
||||
set description of e to newDescription
|
||||
end if
|
||||
|
||||
if newAllDay is "true" then
|
||||
set allday event of e to true
|
||||
else if newAllDay is "false" then
|
||||
set allday event of e to false
|
||||
end if
|
||||
|
||||
if newRecurrence is not "" then
|
||||
set recurrence of e to newRecurrence
|
||||
end if
|
||||
|
||||
return "Updated event: " & eventUID
|
||||
end if
|
||||
end try
|
||||
end repeat
|
||||
|
||||
return "Error: Event with UID '" & eventUID & "' not found"
|
||||
end tell
|
||||
end run
|
||||
EOF
|
||||
Reference in New Issue
Block a user