> ## Documentation Index
> Fetch the complete documentation index at: https://developer.box.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect Bot to Box

export const ProgressBar = ({pages = [], ...props}) => {
  const [currentStep, setCurrentStep] = useState(0);
  const [isDarkMode, setIsDarkMode] = useState(false);
  useEffect(() => {
    const checkDarkMode = () => {
      const isDark = document.documentElement.classList.contains('dark');
      console.log('ProgressBar - isDarkMode:', isDark);
      setIsDarkMode(isDark);
    };
    checkDarkMode();
    const observer = new MutationObserver(() => {
      checkDarkMode();
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => {
      observer.disconnect();
    };
  }, []);
  useEffect(() => {
    if (pages.length > 0) {
      const currentPath = window.location.pathname;
      const stepIndex = pages.findIndex(page => {
        const pagePath = page.startsWith('/') ? page : `/${page}`;
        return currentPath.endsWith(pagePath) || currentPath.includes(pagePath);
      });
      if (stepIndex !== -1) {
        setCurrentStep(stepIndex + 1);
      }
    }
  }, [pages]);
  if (!pages || pages.length === 0) {
    return null;
  }
  const step = currentStep;
  const total = pages.length;
  console.log('ProgressBar - Rendering with isDarkMode:', isDarkMode);
  const progressBarContainerStyle = {
    width: '100%',
    marginBottom: '32px',
    display: 'flex',
    alignItems: 'center',
    gap: '16px'
  };
  const stepsContainerStyle = {
    display: 'flex',
    alignItems: 'center',
    gap: '8px',
    flexShrink: 0
  };
  const progressBarTrackStyle = {
    flex: 1,
    height: '22px',
    backgroundColor: 'rgba(169, 210, 244, 0.06)',
    border: isDarkMode ? '1px solid rgba(230, 241, 247, 0.67)' : '1px solid #e3ecf3',
    borderRadius: '4px',
    overflow: 'hidden',
    position: 'relative'
  };
  const progressBarFillStyle = {
    height: '100%',
    backgroundColor: 'rgba(113, 192, 248, 0.23)',
    width: `${step / total * 100}%`,
    transition: 'width 0.3s ease'
  };
  const getStepStyle = (stepNumber, isActive) => {
    if (isDarkMode) {
      return {
        width: '22px',
        height: '22px',
        borderRadius: '4px',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: '12px',
        fontWeight: '600',
        position: 'relative',
        zIndex: 1,
        transition: 'all 0.3s ease',
        backgroundColor: isActive ? 'rgba(113, 192, 248, 0.23)' : 'transparent',
        color: isActive ? '#60a5fa' : '#a0aec0',
        border: isActive ? '1px solid #e3ecf3' : '1px solid #e0e6eb',
        cursor: 'pointer',
        textDecoration: 'none'
      };
    }
    if (isActive) {
      return {
        width: '22px',
        height: '22px',
        borderRadius: '4px',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: '12px',
        fontWeight: '600',
        position: 'relative',
        zIndex: 1,
        transition: 'all 0.3s ease',
        backgroundColor: 'rgba(169, 210, 244, 0.32)',
        color: '#374151',
        border: '1px solid #e1eef8',
        cursor: 'pointer',
        textDecoration: 'none'
      };
    }
    return {
      width: '22px',
      height: '22px',
      borderRadius: '4px',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      fontSize: '12px',
      fontWeight: '600',
      position: 'relative',
      zIndex: 1,
      transition: 'all 0.3s ease',
      backgroundColor: '#fbfbfb',
      color: '#9ca3af',
      border: '1px solid #e3ecf3',
      cursor: 'pointer',
      textDecoration: 'none'
    };
  };
  return <div style={progressBarContainerStyle} {...props}>
      <div style={stepsContainerStyle}>
        {Array.from({
    length: total
  }, (_, index) => {
    const stepNumber = index + 1;
    const pageIndex = index;
    const pagePath = pages[pageIndex];
    const fullPath = pagePath.startsWith('/') ? pagePath : `/${pagePath}`;
    const isActive = stepNumber === step;
    return <a key={stepNumber} href={fullPath} style={getStepStyle(stepNumber, isActive)}>
              {stepNumber}
            </a>;
  })}
      </div>
      <div style={progressBarTrackStyle}>
        <div style={progressBarFillStyle}></div>
      </div>
    </div>;
};

export const ChoiceDebug = ({option}) => {
  const [currentValue, setCurrentValue] = useState(null);
  const [allState, setAllState] = useState({});
  const [isDarkMode, setIsDarkMode] = useState(false);
  useEffect(() => {
    const updateState = () => {
      if (window.choiceStateManager) {
        setCurrentValue(window.choiceStateManager.getValue(option));
        setAllState(window.choiceStateManager.getState());
      }
    };
    updateState();
    const unsubscribe = window.listenToChoice?.(option, updateState) || (() => {});
    const handleGlobalUpdate = () => updateState();
    window.addEventListener("choiceStateUpdate", handleGlobalUpdate);
    return () => {
      unsubscribe();
      window.removeEventListener("choiceStateUpdate", handleGlobalUpdate);
    };
  }, [option]);
  useEffect(() => {
    const checkDarkMode = () => {
      if (document.documentElement.classList.contains("dark")) {
        setIsDarkMode(true);
      } else if (document.documentElement.classList.contains("light")) {
        setIsDarkMode(false);
      } else {
        setIsDarkMode(window.matchMedia("(prefers-color-scheme: dark)").matches);
      }
    };
    checkDarkMode();
    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
    const handleMediaChange = e => {
      if (!document.documentElement.classList.contains("dark") && !document.documentElement.classList.contains("light")) {
        setIsDarkMode(e.matches);
      }
    };
    mediaQuery.addEventListener("change", handleMediaChange);
    const observer = new MutationObserver(() => {
      checkDarkMode();
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => {
      mediaQuery.removeEventListener("change", handleMediaChange);
      observer.disconnect();
    };
  }, []);
  return <div style={{
    padding: "10px",
    backgroundColor: isDarkMode ? "#2d3748" : "#f5f5f5",
    border: isDarkMode ? "1px solid #4a5568" : "1px solid #ddd",
    borderRadius: "4px",
    fontSize: "12px",
    fontFamily: "monospace",
    marginTop: "20px",
    color: isDarkMode ? "#e2e8f0" : "inherit"
  }}>
      <strong>Choice Debug:</strong>
      <br />
      Option: {option}
      <br />
      Current Value: {currentValue || "undefined"}
      <br />
      All State: {JSON.stringify(allState, null, 2)}
    </div>;
};

export const Observe = ({option, value, children, ...props}) => {
  const [shouldShow, setShouldShow] = useState(false);
  useEffect(() => {
    const updateVisibility = () => {
      const matches = window.matchesChoiceValues?.(option, value) || false;
      setShouldShow(matches);
    };
    updateVisibility();
    const unsubscribe = window.listenToChoice?.(option, updateVisibility) || (() => {});
    return unsubscribe;
  }, [option, value]);
  if (!shouldShow) {
    return null;
  }
  return <div {...props}>{children}</div>;
};

export const Trigger = ({option, value, children, ...props}) => {
  const handleClick = () => {
    window.triggerChoice?.(option, value);
  };
  return <div onClick={handleClick} style={{
    cursor: "pointer"
  }} {...props}>
      {children}
    </div>;
};

export const Grid = ({columns = 2, compact = false, children, ...props}) => {
  const gridStyles = {
    display: "grid",
    gridTemplateColumns: `repeat(${columns}, 1fr)`,
    gap: compact ? "8px" : "16px",
    marginBottom: compact ? "10px" : "20px"
  };
  return <div style={gridStyles} {...props}>
      {children}
    </div>;
};

export const Choice = ({option, value, color = "", unset = false, lazy = false, children, ...props}) => {
  const [shouldShow, setShouldShow] = useState(false);
  const [hasEverShown, setHasEverShown] = useState(false);
  const [isDarkMode, setIsDarkMode] = useState(false);
  useEffect(() => {
    const updateVisibility = () => {
      const hasOptionValue = window.hasChoiceValue?.(option) || false;
      const matchesValue = window.matchesChoiceValues?.(option, value) || false;
      let show = false;
      if (unset && !hasOptionValue) {
        show = true;
      } else if (!unset && matchesValue) {
        show = true;
      }
      setShouldShow(show);
      if (show && !hasEverShown) {
        setHasEverShown(true);
      }
    };
    updateVisibility();
    const unsubscribe = window.listenToChoice?.(option, updateVisibility) || (() => {});
    return unsubscribe;
  }, [option, value, unset, hasEverShown]);
  useEffect(() => {
    const checkDarkMode = () => {
      if (document.documentElement.classList.contains("dark")) {
        setIsDarkMode(true);
      } else if (document.documentElement.classList.contains("light")) {
        setIsDarkMode(false);
      } else {
        setIsDarkMode(window.matchMedia("(prefers-color-scheme: dark)").matches);
      }
    };
    checkDarkMode();
    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
    const handleMediaChange = e => {
      if (!document.documentElement.classList.contains("dark") && !document.documentElement.classList.contains("light")) {
        setIsDarkMode(e.matches);
      }
    };
    mediaQuery.addEventListener("change", handleMediaChange);
    const observer = new MutationObserver(() => {
      checkDarkMode();
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => {
      mediaQuery.removeEventListener("change", handleMediaChange);
      observer.disconnect();
    };
  }, []);
  const getColorStyles = () => {
    const baseStyles = {
      border: isDarkMode ? "1px dashed #4a5568" : "1px dashed #e1e5e9",
      padding: "20px",
      marginBottom: "20px",
      borderRadius: "8px",
      backgroundColor: isDarkMode ? "#1a202c" : "#ffffff"
    };
    const colorMap = {
      green: {
        light: {
          backgroundColor: "#d4edda",
          borderColor: "#28a745"
        },
        dark: {
          backgroundColor: "#1a3a2a",
          borderColor: "#66bb6a"
        }
      },
      red: {
        light: {
          backgroundColor: "#f8d7da",
          borderColor: "#dc3545"
        },
        dark: {
          backgroundColor: "#3a1a1a",
          borderColor: "#ef5350"
        }
      },
      blue: {
        light: {
          backgroundColor: "#d1ecf1",
          borderColor: "#0c5460"
        },
        dark: {
          backgroundColor: "#1a2a3a",
          borderColor: "#42a5f5"
        }
      },
      none: {
        backgroundColor: "transparent",
        padding: "0",
        margin: "0",
        border: "none"
      }
    };
    const colorStyles = color !== "none" ? colorMap[color]?.[isDarkMode ? "dark" : "light"] || ({}) : colorMap.none;
    return {
      ...baseStyles,
      ...colorStyles
    };
  };
  if (lazy && !hasEverShown && !shouldShow) {
    return null;
  }
  return <div style={{
    ...getColorStyles(),
    display: shouldShow ? "block" : "none"
  }} className="choice-content" {...props}>
      {children}
    </div>;
};

export const Choose = ({option, value, color = "", children, ...props}) => {
  const [isSelected, setIsSelected] = useState(false);
  const [hasOptionTriggered, setHasOptionTriggered] = useState(false);
  const [isDarkMode, setIsDarkMode] = useState(false);
  useEffect(() => {
    const currentValue = window.getChoiceValue?.(option);
    const optionTriggered = window.hasChoiceValue?.(option) || false;
    setIsSelected(currentValue === value);
    setHasOptionTriggered(optionTriggered);
    const unsubscribe = window.listenToChoice?.(option, newValue => {
      setIsSelected(newValue === value);
      setHasOptionTriggered(true);
    }) || (() => {});
    return unsubscribe;
  }, [option, value]);
  useEffect(() => {
    const checkDarkMode = () => {
      if (document.documentElement.classList.contains("dark")) {
        setIsDarkMode(true);
      } else if (document.documentElement.classList.contains("light")) {
        setIsDarkMode(false);
      } else {
        setIsDarkMode(window.matchMedia("(prefers-color-scheme: dark)").matches);
      }
    };
    checkDarkMode();
    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
    const handleMediaChange = e => {
      if (!document.documentElement.classList.contains("dark") && !document.documentElement.classList.contains("light")) {
        setIsDarkMode(e.matches);
      }
    };
    mediaQuery.addEventListener("change", handleMediaChange);
    const observer = new MutationObserver(() => {
      checkDarkMode();
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => {
      mediaQuery.removeEventListener("change", handleMediaChange);
      observer.disconnect();
    };
  }, []);
  const handleClick = () => {
    window.triggerChoice?.(option, value);
  };
  const handleKeyDown = e => {
    if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      handleClick();
    }
  };
  const getColorStyles = () => {
    const baseStyles = {
      border: isDarkMode ? "1px dashed #4a5568" : "1px dashed #e1e5e9",
      cursor: "pointer",
      padding: "20px",
      position: "relative",
      backgroundColor: isDarkMode ? "#2d3748" : "#f8f9fa",
      outline: "none",
      height: "100%",
      borderRadius: "8px",
      transition: "all 0.2s ease",
      display: "flex",
      flexDirection: "column"
    };
    const colorMap = {
      green: {
        light: {
          backgroundColor: "#d4edda",
          borderColor: "#28a745"
        },
        dark: {
          backgroundColor: "#1a3a2a",
          borderColor: "#66bb6a"
        }
      },
      red: {
        light: {
          backgroundColor: "#f8d7da",
          borderColor: "#dc3545"
        },
        dark: {
          backgroundColor: "#3a1a1a",
          borderColor: "#ef5350"
        }
      },
      blue: {
        light: {
          backgroundColor: "#d1ecf1",
          borderColor: "#0c5460"
        },
        dark: {
          backgroundColor: "#1a2a3a",
          borderColor: "#42a5f5"
        }
      }
    };
    const colorStyles = colorMap[color]?.[isDarkMode ? "dark" : "light"] || ({});
    if (isSelected) {
      return {
        ...baseStyles,
        ...colorStyles,
        borderStyle: "solid",
        borderWidth: "3px",
        borderColor: colorStyles.borderColor || (isDarkMode ? "#42a5f5" : "#0061d5"),
        backgroundColor: colorStyles.backgroundColor || (isDarkMode ? "#1a2a3a" : "#e3f2fd"),
        boxShadow: isDarkMode ? "0 2px 8px rgba(66, 165, 245, 0.3)" : "0 2px 8px rgba(0, 97, 213, 0.3)",
        transform: "scale(1.02)"
      };
    }
    if (hasOptionTriggered && !isSelected) {
      return {
        ...baseStyles,
        ...colorStyles,
        opacity: 0.5
      };
    }
    return {
      ...baseStyles,
      ...colorStyles
    };
  };
  const iconStyles = {
    float: "left",
    position: "relative",
    top: "2px",
    marginRight: "12px",
    width: "20px",
    height: "20px",
    color: isSelected ? isDarkMode ? "#42a5f5" : "#0061d5" : isDarkMode ? "#a0aec0" : "#666"
  };
  return <div onClick={handleClick} style={getColorStyles()} tabIndex={0} onKeyDown={handleKeyDown} {...props}>
      <div style={iconStyles}>
        {isSelected ? <svg viewBox="0 0 24 24" fill="currentColor" style={{
    width: "100%",
    height: "100%"
  }}>
            <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
          </svg> : <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" style={{
    width: "100%",
    height: "100%"
  }}>
            <circle cx="12" cy="12" r="10" />
          </svg>}
      </div>
      <div style={{
    flex: 1
  }} className="choose-content">
        {children}
      </div>
    </div>;
};

export const Link = ({href, children, className, ...props}) => {
  const [localizedHref, setLocalizedHref] = useState(href);
  const supportedLocales = useMemo(() => ['ja'], []);
  useEffect(() => {
    const getLocaleFromPath = path => {
      const match = path.match(/^\/([a-z]{2})(?:\/|$)/);
      if (match) {
        const potentialLocale = match[1];
        if (supportedLocales.includes(potentialLocale)) {
          return potentialLocale;
        }
      }
      return null;
    };
    const hasLocalePrefix = path => {
      const match = path.match(/^\/([a-z]{2})(?:\/|$)/);
      return match ? supportedLocales.includes(match[1]) : false;
    };
    const currentPath = window.location.pathname;
    const currentLocale = getLocaleFromPath(currentPath);
    if (href && href.startsWith('/') && !hasLocalePrefix(href)) {
      if (currentLocale) {
        setLocalizedHref(`/${currentLocale}${href}`);
      } else {
        setLocalizedHref(href);
      }
    } else {
      setLocalizedHref(href);
    }
  }, [href, supportedLocales]);
  return <a href={localizedHref} className={className} {...props}>
      {children}
    </a>;
};

<ProgressBar
  pages={[
                        "guides/collaborations/connect-slack-to-group-collabs/configure-slack",
                        "guides/collaborations/connect-slack-to-group-collabs/configure-box",
                        "guides/collaborations/connect-slack-to-group-collabs/scaffold-application-code",
                        "guides/collaborations/connect-slack-to-group-collabs/handle-slack-events",
                        "guides/collaborations/connect-slack-to-group-collabs/connect-box-functions",
                        "guides/collaborations/connect-slack-to-group-collabs/test-bot"
                      ]}
/>

We're now handling and processing events coming from Slack, then obtaining all
information needed to connect with Box users and groups. We now need to
connect that functionality to Box functions.

In this step we'll expand several functions from the last step to incorporate
new Box features.

* Instantiate a Box client.
* Add a Box user to a Box group.
* Remove a Box user from a Box group.
* Fetch a Box group ID from a group name.
* Add content that is shared to a group.

## Instantiate a Box Client

To make calls to the Box APIs, you'll first need to set up a Box client.

<Choice option="programming.platform" value="node" color="none">
  Within `process.js`, replace the `// INSTANTIATE BOX CLIENT` comment at the top
  with the following.

  ```js  theme={null}
  const boxConfig = require("./boxConfig.json");
  const sdk = box.getPreconfiguredInstance(boxConfig);
  const client = sdk.getAppAuthClient("enterprise");
  ```

  The `boxConfig` assignment line will use the `boxConfig.json` file you
  downloaded from your Box app at the end of <Link href="/guides/collaborations/connect-slack-to-group-collabs/configure-box">step 2</Link>. The sample above is
  assuming that you have it stored in the same folder as `process.js`. If that's
  not the case, change the path to point to where your `boxConfig.json` file is,
  and what it may be named.

  The last `client` assignment line is creating a Box client object which may be
  used to make API calls. At this point it is scoped to the
  <Link href="/platform/user-types/#service-account/">service account</Link> of the application, and not a specific user.
</Choice>

<Choice option="programming.platform" value="java" color="none">
  Within `Application.java`, replace the `// INSTANTIATE BOX CLIENT` comment
  within the `processEvent` method with the following.

  ```java  theme={null}
  this.fileReader = new FileReader("boxConfig.json");
  this.boxConfig = BoxConfig.readFrom(fileReader);
  this.boxAPI = BoxDeveloperEditionAPIConnection.getAppEnterpriseConnection(boxConfig);
  ```

  The `boxConfig` assignment line will use the `boxConfig.json` file you
  downloaded from your Box app at the end of <Link href="/guides/collaborations/connect-slack-to-group-collabs/configure-box">step 2</Link>. The sample above is
  assuming that you have it stored at the root of the Java project. If that's
  not the case, change the path in the `fileReader` assignment to point to where
  your `boxConfig.json` file is, and what it may be named.

  The last `boxAPI` assignment line is creating a Box client object which may be
  used to make API calls. At this point it is scoped to the
  <Link href="/platform/user-types/#service-account/">service account</Link> of the application, and not a specific user.
</Choice>

<Choice option="programming.platform" unset color="none">
  <Danger>
    **Incomplete previous step**
    Please select a preferred language / framework in step 1 to get started.
  </Danger>
</Choice>

## Add a Box user to a group

Let's add a function that adds a Box user to a group. When a bot is added to a
channel and needs to create a Box group with all users of the channel, or when
a single user joins the channel after that action, this function will perform
that task.

<Choice option="programming.platform" value="node" color="none">
  Replace the `addGroupUser` function with the following.

  ```js  theme={null}
  function addGroupUser(groupId, email) {
      client.enterprise.getUsers({ filter_term: email }).then((users) => {
          if (users.entries.length > 0) {
              const userId = users.entries[0].id;
              const groupRole = client.groups.userRoles.MEMBER;

              client.groups
                  .addUser(groupId, userId, { role: groupRole })
                  .then((membership) => {
                      if (membership.id) {
                          console.log(`Member added with membership ID: ${membership.id}`);
                      } else {
                          console.log(`Member not added`);
                      }
                  })
                  .catch(function (err) {
                      console.log(err.response.body);
                  });
          } else {
              console.log("No Box user found to add to group");
          }
      });
  }
  ```
</Choice>

<Choice option="programming.platform" value="java" color="none">
  Replace the `addGroupUser` method with the following.

  ```java  theme={null}
  public void addGroupUser(String groupId, String userEmail) {
      Iterable<BoxUser.Info> users = BoxUser.getAllEnterpriseUsers(this.boxAPI, userEmail);

      for (BoxUser.Info user : users) {
          if (user.getLogin().toUpperCase().equals(userEmail.toUpperCase())) {
              try {
                  BoxGroup group = new BoxGroup(boxAPI, groupId);
                  BoxUser boxUser = new BoxUser(this.boxAPI, user.getID());
                  BoxGroupMembership.Info groupMembershipInfo = group.addMembership(boxUser);
              } catch (Exception ex) {
                  System.err.println("User already present");
              }
          }
      }
  }
  ```
</Choice>

<Choice option="programming.platform" unset color="none">
  <Danger>
    **Incomplete previous step**
    Please select a preferred language / framework in step 1 to get started.
  </Danger>
</Choice>

Since we're matching a Slack user to a Box user via their email address, we
first find a matching Box user using the Slack profile email. If found, a call
is made to add that user to the channel group. The group was created when the
bot was first added.

<Tip>
  The Box <Link href="/reference/get-users-id">Get User</Link> endpoint only permits user lookup
  by user ID. To lookup a user by email address, use the
  <Link href="/reference/get-users">List Enterprise Users</Link> endpoint and set the
  `filter_term` option to the email address you're searching for.
</Tip>

## Remove a Box user to a group

When a user leaves or is removed from a Slack channel, we also want to remove
them from the Box group so that they can no longer access the shared group
content.

<Choice option="programming.platform" value="node" color="none">
  Replace the `removeGroupUser` function with the following.

  ```js  theme={null}
  function removeGroupUser(groupId, email) {
      client.groups.getMemberships(groupId).then(memberships => {
          for (let i = 0; i < memberships.entries.length; i++) {
              if (memberships.entries[i].user.login === email) {
                  client.groups
                  .removeMembership(memberships.entries[i].id)
                  .then(() => {
                      console.log('Group user removed')
                  });
                  break;
              }
          }
      });
  }
  ```
</Choice>

<Choice option="programming.platform" value="java" color="none">
  Replace the `removeGroupUser` method with the following.

  ```java  theme={null}
  public void removeGroupUser(String groupId, String userEmail) {
    BoxGroup boxGroup = new BoxGroup(this.boxAPI, groupId);
    Iterable<BoxGroupMembership.Info> memberships = boxGroup.getAllMemberships();
    for (BoxGroupMembership.Info membershipInfo : memberships) {
      if (membershipInfo.getUser().getLogin().toUpperCase().equals(userEmail.toUpperCase())) {
        BoxGroupMembership membership = new BoxGroupMembership(this.boxAPI, membershipInfo.getID());
        membership.delete();
      }
    }
  }
  ```
</Choice>

<Choice option="programming.platform" unset color="none">
  <Danger>
    **Incomplete previous step**
    Please select a preferred language / framework in step 1 to get started.
  </Danger>
</Choice>

This code will take the group ID, which will be the Slack channel ID, and get
all members of the group. If a matching member is found for the person that
left the Slack channel, based on email address, that person is removed from the
group using their membership ID.

<Tip>
  **Improving performance with a data store**

  While looking up group memberships to obtain a membership ID negates the need
  to store membership IDs in a local data store (like a database), this code
  can be made more efficient by having a data store that saves the Box
  membership ID with the user record.

  By using a local data store, the membership ID can be retrieved from the data
  store rather than having to call the Box API repeatedly to search for the
  membership ID.
</Tip>

## Fetch a Box group ID for a group name

The next Box function we need has two main purposes.

* Return the Box group ID of an existing group.
* If a group doesn't exist, create the Box group and return the ID.

<Choice option="programming.platform" value="node" color="none">
  Replace the `getGroupId` function with the following.

  ```js  theme={null}
  function getGroupId(groupName, callback) {
      client.groups.getAll().then((groups) => {
          const group = groups.entries.filter((g) => g.name === groupName)[0];

          if (!group) {
              client.groups
                .create(groupName, {
                    description: "Slack channel collaboration group",
                    invitability_level: "all_managed_users",
                })
                .then((group) => {
                    callback(group.id);
                });
          } else {
              callback(group.id);
          }
      });
  }
  ```
</Choice>

<Choice option="programming.platform" value="java" color="none">
  Replace the `getGroupId` method with the following.

  ```java  theme={null}
  public String getGroupId(String groupName) {
      String groupId = new String();

      Iterable<BoxGroup.Info> groups = BoxGroup.getAllGroups(this.boxAPI);
      for (BoxGroup.Info groupInfo : groups) {
          if (groupInfo.getName().toUpperCase().equals(groupName)) {
              groupId = groupInfo.getID();
          }
      }

      if (groupId.isEmpty()) {
          BoxGroup.Info groupInfo = BoxGroup.createGroup(boxAPI, groupName);
          groupId = groupInfo.getID();
      }

      return groupId;
  }
  ```
</Choice>

<Choice option="programming.platform" unset color="none">
  <Danger>
    **Incomplete previous step**
    Please select a preferred language / framework in step 1 to get started.
  </Danger>
</Choice>

The code fetches all the groups in the enterprise, and then tries to match the
Slack channel ID to the group name. If any of the groups matches, the group ID
is returned.

If there are no matches, a new Box group is created and the ID of the group is
returned for use. The group will be named after the Slack channel ID since that
is a constant that is returned with both slash commands and user events, making
it easier to lookup without additional functions.

## Add shared content to a group

Finally, the main purpose of our whole application is to allow users to share
files and folders from their own Box accounts with everyone else in the group.

Building upon all previous functionality, the following function performs that
task.

<Choice option="programming.platform" value="node" color="none">
  Replace the `processContent` function with the following.

  ```js  theme={null}
  function processContent(user, channel, itemType, itemId) {
      getGroupId(channel, function (groupId) {
          const email = user.profile.email;

          client.enterprise.getUsers({ filter_term: email }).then((users) => {
              if (users.entries.length > 0) {
                  client.asUser(users.entries[0].id);
                  const collabRole = client.collaborationRoles.VIEWER;
                  const collabOptions = { type: itemType };

                  client.collaborations
                      .createWithGroupID(groupId, itemId, collabRole, collabOptions)
                      .then((collaboration) => {
                          console.log(
                              `Content added with collaboration ID ${collaboration.id}`
                          );
                      })
                      .catch(function (err) {
                          console.log(
                            util.inspect(err.response.body, {
                                showHidden: false,
                                depth: null,
                            })
                          );
                      });
              }
          });
      });
  }
  ```
</Choice>

<Choice option="programming.platform" value="java" color="none">
  Replace the `processContent` method with the following.

  ```java  theme={null}
  public void processContent(JSONObject userResponse, String channel, String fType, String fId) {
      String groupId = getGroupId(channel);

      JSONObject userObj = (JSONObject) userResponse.get("user");
      JSONObject userProfile = (JSONObject) userObj.get("profile");
      String userEmail = (String) userProfile.get("email");

      Iterable<BoxUser.Info> users = BoxUser.getAllEnterpriseUsers(this.boxAPI, userEmail);

      for (BoxUser.Info user : users) {
          if (user.getLogin().toUpperCase().equals(userEmail.toUpperCase())) {
              String uid = user.getID();
              boxAPI.asUser(uid);

              BoxCollaborator collabGroup = new BoxGroup(boxAPI, groupId);

              try {
                  if (fType.equals("file")) {
                      BoxFile file = new BoxFile(boxAPI, fId);
                      file.collaborate(collabGroup, BoxCollaboration.Role.VIEWER, false, false);
                  } else if (fType.equals("folder")) {
                      BoxFolder folder = new BoxFolder(boxAPI, fId);
                      folder.collaborate(collabGroup, BoxCollaboration.Role.VIEWER);
                  }
              } catch (Exception ex) {
                  System.err.println("Collaboration failed");
              }

              boxAPI.asSelf();
          }
      }
  }
  ```
</Choice>

<Choice option="programming.platform" unset color="none">
  <Danger>
    **Incomplete previous step**
    Please select a preferred language / framework in step 1 to get started.
  </Danger>
</Choice>

The code starts by capturing the Box group ID for the Slack channel, which
is where content will be shared to.

Since we want to share files and folders from the Box account of the person who
sent the slash command, we next capture their Box user profile based on their
email address.

Lastly, we make a call to collaborate content with the group via the group ID.

## Summary

* You've instantiated a Box client
* You've created Box group user add and remove functions.
* You've created a function to share content with the group.

<Observe option="programming.platform" value="node,java">
  <Next>I've set up my Box functions</Next>
</Observe>
