> ## 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.

# 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>{translate("Choice Debug:")}</strong>
      <br />
      {translate("Option:")} {option}
      <br />
      {translate("Current Value:")} {currentValue || "undefined"}
      <br />
      {translate("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 = localizeLink(href);
  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"
                  ]}
/>

ここでは、Slackから送信されるイベントを処理し、Boxのユーザーやグループとの接続に必要なすべての情報を取得します。その機能をBoxの関数に関連付ける必要があります。

この手順では、直前の手順で説明した関数をいくつか拡張し、新しいBox機能を組み込みます。

* Boxクライアントをインスタンス化する
* BoxグループにBoxユーザーを追加する
* BoxグループからBoxユーザーを削除する
* グループ名からBoxグループIDを取得する
* グループと共有するコンテンツを追加する

## Boxクライアントのインスタンス化

Box APIを呼び出すには、最初にBoxクライアントを設定する必要があります。

<Choice option="programming.platform" value="node" color="none">
  `process.js`で、先頭にある`// INSTANTIATE BOX CLIENT`コメントを次の内容に置き換えます。

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

  `boxConfig`の代入行では、<Link href="/guides/collaborations/connect-slack-to-group-collabs/configure-box">手順2</Link>の最後でBoxアプリからダウンロードした`boxConfig.json`ファイルを使用します。上記のサンプルでは、ファイルを`process.js`と同じフォルダに保存していることを前提としています。そうではない場合は、`boxConfig.json`ファイルの場所を指すパスとファイル名に変更してください。

  最後の`client`の代入行では、APIコールに使用できるBoxクライアントオブジェクトを作成します。この時点では、その対象範囲は特定のユーザーではなく、アプリケーションの<Link href="/platform/user-types/#service-account/">サービスアカウント</Link>に設定されています。
</Choice>

<Choice option="programming.platform" value="java" color="none">
  `Application.java`で、`processEvent`メソッド内の`// INSTANTIATE BOX CLIENT`コメントを次の内容に置き換えます。

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

  `boxConfig`の代入行では、<Link href="/guides/collaborations/connect-slack-to-group-collabs/configure-box">手順2</Link>の最後でBoxアプリからダウンロードした`boxConfig.json`ファイルを使用します。上記のサンプルでは、ファイルをJavaプロジェクトのルートに保存していることを前提としています。そうではない場合は、`fileReader`の代入行のパスを、`boxConfig.json`ファイルの場所を指すパスとファイル名に変更してください。

  最後の`boxAPI`の代入行では、APIコールに使用できるBoxクライアントオブジェクトを作成します。この時点では、その対象範囲は特定のユーザーではなく、アプリケーションの<Link href="/platform/user-types/#service-account/">サービスアカウント</Link>に設定されています。
</Choice>

<Choice option="programming.platform" unset color="none">
  <Danger>
    **前の手順が完了していません**最初に、手順1でお好みの言語/フレームワークを選択してください。
  </Danger>
</Choice>

## グループへのBoxユーザーの追加

グループにBoxユーザーを追加する関数を追加します。ボットがチャンネルに追加され、チャンネルのすべてのユーザーを含むBoxグループの作成が必要になった場合、またはその操作の後に1人のユーザーがチャンネルに参加した場合に、この関数によってそのタスクが実行されます。

<Choice option="programming.platform" value="node" color="none">
  `addGroupUser`関数を次の内容に置き換えます。

  ```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">
  `addGroupUser`メソッドを次の内容に置き換えます。

  ```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>
    **前の手順が完了していません**最初に、手順1でお好みの言語/フレームワークを選択してください。
  </Danger>
</Choice>

メールアドレスを使用してSlackユーザーをBoxユーザーと照合しているため、最初に、Slackのプロフィールのメールアドレスを使用して一致するBoxユーザーを検索します。見つかると、そのユーザーをチャンネルのグループに追加するための呼び出しが行われます。このグループは、ボットが最初に追加されたときに作成されています。

<Tip>
  Boxの<Link href="/reference/get-users-id">ユーザーを取得</Link>エンドポイントでは、ユーザーIDによるユーザー検索のみ許可されています。メールアドレスでユーザーを検索するには、<Link href="/reference/get-users">会社ユーザーのリストを取得</Link>エンドポイントを使用し、`filter_term`オプションを検索対象のメールアドレスに設定します。
</Tip>

## グループからのBoxユーザーの削除

Slackチャンネルから退出したユーザーや削除されたユーザーは、共有グループコンテンツにアクセスできなくなるようにBoxグループから削除することもできます。

<Choice option="programming.platform" value="node" color="none">
  `removeGroupUser`関数を次の内容に置き換えます。

  ```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">
  `removeGroupUser`メソッドを次の内容に置き換えます。

  ```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>
    **前の手順が完了していません**最初に、手順1でお好みの言語/フレームワークを選択してください。
  </Danger>
</Choice>

このコードでは、SlackのチャンネルIDとなるグループIDを取得し、グループの全メンバーを取得します。メールアドレスに基づいて、Slackチャンネルを退出したユーザーに一致するメンバーが見つかると、そのユーザーはそのメンバーシップIDを使用してグループから削除されます。

<Tip>
  **データストアによるパフォーマンスの向上**

  グループメンバーシップを検索してメンバーシップIDを取得すると、ローカルのデータストア (データベースなど) にメンバーシップIDを保存する必要はなくなりますが、ユーザーレコードとともにBoxメンバーシップIDを保存するデータストアがあれば、このコードがより効率的なものになります。

  ローカルのデータストアを使用すると、メンバーシップIDは、そのデータストアから取得できます。Box APIを繰り返し呼び出してメンバーシップIDを検索する必要はありません。
</Tip>

## グループ名に対応したBoxグループIDの取得

次に必要なBox関数には、主に2つの目的があります。

* 既存グループのBoxグループIDを返します。
* グループが存在しない場合、Boxグループを作成してそのIDを返します。

<Choice option="programming.platform" value="node" color="none">
  `getGroupId`関数を次の内容に置き換えます。

  ```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">
  `getGroupId`メソッドを次の内容に置き換えます。

  ```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>
    **前の手順が完了していません**最初に、手順1でお好みの言語/フレームワークを選択してください。
  </Danger>
</Choice>

このコードでは、社内のすべてのグループを取得し、SlackチャンネルIDとグループ名の照合を試みます。いずれかのグループが一致すると、そのグループIDが返されます。

一致するものがない場合は、新しいBoxグループが作成され、そのグループのIDが返されます。グループの名前はSlackチャンネルIDに基づいて付けられます。これはスラッシュコマンドとUser Eventの両方で返される定数であり、追加の関数がなくても簡単に検索できるようにするためです。

## グループへの共有コンテンツの追加

最終的に、このアプリケーション全体の主な目的は、ユーザーが自分のBoxアカウントにあるファイルやフォルダをグループ内の他のユーザー全員と共有できるようにすることです。

ここまでのすべての機能を基に、次の関数でそのタスクを実行します。

<Choice option="programming.platform" value="node" color="none">
  `processContent`関数を次の内容に置き換えます。

  ```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">
  `processContent`メソッドを次の内容に置き換えます。

  ```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>
    **前の手順が完了していません**最初に、手順1でお好みの言語/フレームワークを選択してください。
  </Danger>
</Choice>

このコードでは、最初に、コンテンツの共有先となるSlackチャンネル用にBoxグループIDを取得します。

スラッシュコマンドを送信したユーザーのBoxアカウントからファイルやフォルダを共有するため、次に、そのユーザーのBoxユーザープロフィールをメールアドレスに基づいて取得します。

最後に、グループIDを使用して、コンテンツでグループとコラボレーションするための呼び出しを行います。

## まとめ

* Boxクライアントをインスタンス化しました。
* Boxグループユーザーを追加および削除するための関数を作成しました。
* コンテンツをグループと共有するための関数を作成しました。

<Observe option="programming.platform" value="node,java">
  <Next>Boxの関数を設定しました</Next>
</Observe>
