core/src/main/java/net/fabricmc/discord/bot/command/mod/ActionUtil.java
static void applyAction(ActionType type, int data, long targetId, String duration, String reason, @Nullable String extraBodyDesc, CommandContext context) throws Exception {
		// check for conflict

		int prevId = -1;
		Integer prevResetData = null;

		if (type.hasDuration()) {
			ActiveActionEntry existingAction = ActionQueries.getActiveAction(context.bot().getDatabase(), targetId, type);

			if (existingAction != null) {
				int cmp = existingAction.data() != null ? type.compareData(existingAction.data().data(), data) : 0;

				if (cmp == 0) { // same action is already active
					throw new CommandException("The %s is already %s", type.getKind().id, type.getDesc(false));
				} else if (cmp > 0 && !type.checkData(data, existingAction.data().resetData())) { // downgrade to or below the prev action's reset level, just suspend with no new action
					suspendAction(type, targetId, reason, context);
					return;
				}

				if (ActionQueries.suspendAction(context.bot().getDatabase(), existingAction.id(), context.userId(), "superseded")) {
					context.bot().getActionSyncHandler().onActionSuspension(existingAction.id());
					prevId = existingAction.id();
					prevResetData = existingAction.data().resetData();
				}
			} else if (type.getKind() == Kind.CHANNEL && type.isActive(context.server(), targetId, data, context.bot())) { // channel already set to a higher level outside the bot
				throw new CommandException("The %s is already %s", type.getKind().id, type.getDesc(false));
			}
		}

		// determine duration, creation, expiration

		long durationMs = FormatUtil.parseActionDurationMs(duration, type.hasDuration());
		ActivateResult result;

		long creationTime = System.currentTimeMillis();
		long expirationTime;

		if (type.hasDuration()) {
			expirationTime = durationMs > 0 ? creationTime + durationMs : -1;
		} else {
			expirationTime = 0;
		}

		// message target user (done first since it may no longer be possible after applying the discord action)

		if (type.getKind() == Kind.USER) {
			// TODO: allocate action id first?
			notifyTarget(type, false, null, extraBodyDesc, targetId, creationTime, expirationTime, reason, -1, context.bot(), context.server());
		}

		// apply discord action

		try {
			result = type.activate(context.server(), targetId, false, data, reason, context.bot());
		} catch (DiscordException e) {
			throw new CommandException("Action failed: "+e);
		}

		if (!type.hasDuration() && result.targets() == 0) {
			throw new CommandException("Absent target");
		}

		if (!result.applicable()) {
			throw new CommandException("The action is not applicable to the "+type.getKind().id);
		}

		Integer resetData = prevId >= 0 ? prevResetData : result.resetData();

		// create db record

		ActionEntry entry = ActionQueries.createAction(context.bot().getDatabase(),
				type,
				(data != 0 || result.resetData() != null ? new ActionData(data, resetData) : null),
				targetId, context.userId(), durationMs, creationTime, expirationTime, reason, prevId);

		// apply discord action again in case the user rejoined quickly (race condition)

		if (type.hasDuration()) {
			try {
				type.activate(context.server(), targetId, false, data, reason, context.bot());
			} catch (DiscordException e) {
				LOGGER.warn("Action re-application failed: {}", e.toString());
			}
		}

		// announce action

		announceAction(type, false, null, extraBodyDesc,
				targetId,
				entry.creationTime(), entry.expirationTime(), reason,
				entry.id(),
				context.channel(), context.author().asUser().get(),
				context.bot(), context.server(),
				false);

		// record action for expiration

		context.bot().getActionSyncHandler().onNewAction(entry);
	}
/**
	 * Apply a specific action to a discord user.
	 *
	 * <p>This creates a DB record, logs the action to the current and the log channels, notifies the target user via
	 * direct message and executes the given discord action.
	 *
	 * @param type action type, e.g. kick or ban
	 * @param target target user in any format supported by UserHandler
	 * @param duration duration string, e.g. 4h30, 4h30m, 3w, perm, permanent or null if none (only applicable to duration-less actions)
	 * @param reason reason for the action, may be null for none
	 * @param context command context
	 * @return true if the action was executed successfully
	 */
Apply a specific action to a discord user.
This creates a DB record, logs the action to the current and the log channels, notifies the target user via
direct message and executes the given discord action.

@param type action type, e.g. kick or ban
@param target target user in any format supported by UserHandler
@param duration duration string, e.g. 4h30, 4h30m, 3w, perm, permanent or null if none (only applicable to duration-less actions)
@param reason reason for the action, may be null for none
@param context command context
@return true if the action was executed successfully
Apply a specific action to a discord user . This creates a DB record logs the action to the current and the log channels notifies the target user via direct message and executes the given discord action . @param type action type e . g . kick or ban @param target target user in any format supported by UserHandler @param duration duration string e . g . 4h30 4h30m 3w perm permanent or null if none ( only applicable to duration - less actions ) @param reason reason for the action may be null for none @param context command context @return true if the action was executed successfully
static void applyAction(ActionType type, int data, long targetId, String duration, String reason, @Nullable String extraBodyDesc, CommandContext context) throws Exception {
		int prevId = -1;
		Integer prevResetData = null;
		if (type.hasDuration()) {
			ActiveActionEntry existingAction = ActionQueries.getActiveAction(context.bot().getDatabase(), targetId, type);
			if (existingAction != null) {
				int cmp = existingAction.data() != null ? type.compareData(existingAction.data().data(), data) : 0;
				if (cmp == 0) { 
					throw new CommandException("The %s is already %s", type.getKind().id, type.getDesc(false));
				} else if (cmp > 0 && !type.checkData(data, existingAction.data().resetData())) { 
					suspendAction(type, targetId, reason, context);
					return;
				}
				if (ActionQueries.suspendAction(context.bot().getDatabase(), existingAction.id(), context.userId(), "superseded")) {
					context.bot().getActionSyncHandler().onActionSuspension(existingAction.id());
					prevId = existingAction.id();
					prevResetData = existingAction.data().resetData();
				}
			} else if (type.getKind() == Kind.CHANNEL && type.isActive(context.server(), targetId, data, context.bot())) { 
				throw new CommandException("The %s is already %s", type.getKind().id, type.getDesc(false));
			}
		}
		long durationMs = FormatUtil.parseActionDurationMs(duration, type.hasDuration());
		ActivateResult result;
		long creationTime = System.currentTimeMillis();
		long expirationTime;
		if (type.hasDuration()) {
			expirationTime = durationMs > 0 ? creationTime + durationMs : -1;
		} else {
			expirationTime = 0;
		}
		if (type.getKind() == Kind.USER) {
			notifyTarget(type, false, null, extraBodyDesc, targetId, creationTime, expirationTime, reason, -1, context.bot(), context.server());
		}
		try {
			result = type.activate(context.server(), targetId, false, data, reason, context.bot());
		} catch (DiscordException e) {
			throw new CommandException("Action failed: "+e);
		}
		if (!type.hasDuration() && result.targets() == 0) {
			throw new CommandException("Absent target");
		}
		if (!result.applicable()) {
			throw new CommandException("The action is not applicable to the "+type.getKind().id);
		}
		Integer resetData = prevId >= 0 ? prevResetData : result.resetData();
		ActionEntry entry = ActionQueries.createAction(context.bot().getDatabase(),
				type,
				(data != 0 || result.resetData() != null ? new ActionData(data, resetData) : null),
				targetId, context.userId(), durationMs, creationTime, expirationTime, reason, prevId);
		 again in case the user rejoined quickly (race condition)
		if (type.hasDuration()) {
			try {
				type.activate(context.server(), targetId, false, data, reason, context.bot());
			} catch (DiscordException e) {
				LOGGER.warn("Action re-application failed: {}", e.toString());
			}
		}
		announceAction(type, false, null, extraBodyDesc,
				targetId,
				entry.creationTime(), entry.expirationTime(), reason,
				entry.id(),
				context.channel(), context.author().asUser().get(),
				context.bot(), context.server(),
				false);
		context.bot().getActionSyncHandler().onNewAction(entry);
	}
static void applyAction ( ActionType type , int data , long targetId , String duration , String reason , @ Nullable String extraBodyDesc , CommandContext context ) throws Exception { int prevId = - 1 ; Integer prevResetData = null ; if ( type . hasDuration ( ) ) { ActiveActionEntry existingAction = ActionQueries . getActiveAction ( context . bot ( ) . getDatabase ( ) , targetId , type ) ; if ( existingAction != null ) { int cmp = existingAction . data ( ) != null ? type . compareData ( existingAction . data ( ) . data ( ) , data ) : 0 ; if ( cmp == 0 ) { throw new CommandException ( "The %s is already %s" , type . getKind ( ) . id , type . getDesc ( false ) ) ; } else if ( cmp > 0 && ! type . checkData ( data , existingAction . data ( ) . resetData ( ) ) ) { suspendAction ( type , targetId , reason , context ) ; return ; } if ( ActionQueries . suspendAction ( context . bot ( ) . getDatabase ( ) , existingAction . id ( ) , context . userId ( ) , "superseded" ) ) { context . bot ( ) . getActionSyncHandler ( ) . onActionSuspension ( existingAction . id ( ) ) ; prevId = existingAction . id ( ) ; prevResetData = existingAction . data ( ) . resetData ( ) ; } } else if ( type . getKind ( ) == Kind . CHANNEL && type . isActive ( context . server ( ) , targetId , data , context . bot ( ) ) ) { throw new CommandException ( "The %s is already %s" , type . getKind ( ) . id , type . getDesc ( false ) ) ; } } long durationMs = FormatUtil . parseActionDurationMs ( duration , type . hasDuration ( ) ) ; ActivateResult result ; long creationTime = System . currentTimeMillis ( ) ; long expirationTime ; if ( type . hasDuration ( ) ) { expirationTime = durationMs > 0 ? creationTime + durationMs : - 1 ; } else { expirationTime = 0 ; } if ( type . getKind ( ) == Kind . USER ) { notifyTarget ( type , false , null , extraBodyDesc , targetId , creationTime , expirationTime , reason , - 1 , context . bot ( ) , context . server ( ) ) ; } try { result = type . activate ( context . server ( ) , targetId , false , data , reason , context . bot ( ) ) ; } catch ( DiscordException e ) { throw new CommandException ( "Action failed: " + e ) ; } if ( ! type . hasDuration ( ) && result . targets ( ) == 0 ) { throw new CommandException ( "Absent target" ) ; } if ( ! result . applicable ( ) ) { throw new CommandException ( "The action is not applicable to the " + type . getKind ( ) . id ) ; } Integer resetData = prevId >= 0 ? prevResetData : result . resetData ( ) ; ActionEntry entry = ActionQueries . createAction ( context . bot ( ) . getDatabase ( ) , type , ( data != 0 || result . resetData ( ) != null ? new ActionData ( data , resetData ) : null ) , targetId , context . userId ( ) , durationMs , creationTime , expirationTime , reason , prevId ) ; if ( type . hasDuration ( ) ) { try { type . activate ( context . server ( ) , targetId , false , data , reason , context . bot ( ) ) ; } catch ( DiscordException e ) { LOGGER . warn ( "Action re-application failed: {}" , e . toString ( ) ) ; } } announceAction ( type , false , null , extraBodyDesc , targetId , entry . creationTime ( ) , entry . expirationTime ( ) , reason , entry . id ( ) , context . channel ( ) , context . author ( ) . asUser ( ) . get ( ) , context . bot ( ) , context . server ( ) , false ) ; context . bot ( ) . getActionSyncHandler ( ) . onNewAction ( entry ) ; }
Apply a specific action to a discord user.
Apply a specific action to a discord user .
// check for conflict // same action is already active // downgrade to or below the prev action's reset level, just suspend with no new action // channel already set to a higher level outside the bot // determine duration, creation, expiration // message target user (done first since it may no longer be possible after applying the discord action) // TODO: allocate action id first? // apply discord action // create db record // apply discord action again in case the user rejoined quickly (race condition) // announce action // record action for expiration